From 4f670e053db43160c31f641455350234faf1f083 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Fri, 2 Jan 2004 18:09:29 +0000 Subject: [PATCH] * src/bitmap.c: + Fixed a minor overflow in the saturation computation. + Use a global lookup table for foreground/background colour selection in the bitmap rendering routine. This broke dithering, sorry. --- src/bitmap.c | 387 +++++++++++++++++++++++++------------------ src/caca.c | 4 + src/caca_internals.h | 2 + 3 files changed, 235 insertions(+), 158 deletions(-) diff --git a/src/bitmap.c b/src/bitmap.c index 39b6006..72517fb 100644 --- a/src/bitmap.c +++ b/src/bitmap.c @@ -53,6 +53,44 @@ enum caca_feature _caca_background; enum caca_feature _caca_dithering; enum caca_feature _caca_antialiasing; +/* + * Local variables + */ +#define LOOKUP_VAL 32 +#define LOOKUP_SAT 32 +#define LOOKUP_HUE 16 +static unsigned char lookup_table[LOOKUP_VAL][LOOKUP_SAT][LOOKUP_HUE]; +static enum caca_color lookup_colors[8]; + +static int const hsv_palette[] = +{ + /* hue, saturation, value */ + 0x0, 0x0, 0x0, /* black */ + 0x0, 0x0, 0x5ff, /* 30% */ + 0x0, 0x0, 0x9ff, /* 70% */ + 0x0, 0x0, 0xfff, /* white */ + 0x1000, 0xfff, 0x5ff, /* dark yellow */ + 0x1000, 0xfff, 0xfff, /* light yellow */ + 0x0, 0xfff, 0x5ff, /* dark red */ + 0x0, 0xfff, 0xfff /* light red */ +}; + +#define HSV_XRATIO (5*5) +#define HSV_YRATIO (3*3) +#define HSV_HRATIO (2*2) + +#define HSV_DISTANCE(h, s, v, index) \ + ((HSV_XRATIO * ((v) - hsv_palette[index * 3 + 2]) \ + * ((v) - hsv_palette[index * 3 + 2])) \ + + (hsv_palette[index * 3 + 2] \ + ? (HSV_YRATIO * ((s) - hsv_palette[index * 3 + 1]) \ + * ((s) - hsv_palette[index * 3 + 1])) \ + : 0) \ + + (hsv_palette[index * 3 + 1] \ + ? (HSV_HRATIO * ((h) - hsv_palette[index * 3 + 0]) \ + * ((h) - hsv_palette[index * 3 + 0])) \ + : 0)) + /* * Local prototypes */ @@ -61,7 +99,7 @@ static void mask2shift(unsigned int, int *, int *); static void get_rgba_default(struct caca_bitmap const *, uint8_t *, int, int, unsigned int *, unsigned int *, unsigned int *, unsigned int *); -static void rgb2hsv_default(int, int, int, int *, int *, int *); +static inline void rgb2hsv_default(int, int, int, int *, int *, int *); /* Dithering methods */ static void init_no_dither(int); @@ -297,7 +335,8 @@ static void get_rgba_default(struct caca_bitmap const *bitmap, uint8_t *pixels, } } -static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val) +static inline void rgb2hsv_default(int r, int g, int b, + int *hue, int *sat, int *val) { int min, max, delta; @@ -305,12 +344,12 @@ static void rgb2hsv_default(int r, int g, int b, int *hue, int *sat, int *val) if(min > g) min = g; if(max < g) max = g; if(min > b) min = b; if(max < b) max = b; - delta = max - min; /* 0 - 0xffff */ - *val = max; /* 0 - 0xffff */ + delta = max - min; /* 0 - 0xfff */ + *val = max; /* 0 - 0xfff */ if(delta) { - *sat = 0x1000 * delta / max; /* 0 - 0xfff */ + *sat = 0xfff * delta / max; /* 0 - 0xfff */ /* Generate *hue between 0 and 0x5fff */ if( r == max ) @@ -463,9 +502,6 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, enum caca_color outfg, outbg; char outch; - /* Only used by coloured background */ - int distbg, distfg, dist, hue1, hue2; - r = g = b = a = 0; /* First get RGB */ @@ -479,10 +515,10 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, for(myx = fromx; myx <= tox; myx++) for(myy = fromy; myy <= toy; myy++) - { - dots++; - get_rgba_default(bitmap, pixels, myx, myy, &r, &g, &b, &a); - } + { + dots++; + get_rgba_default(bitmap, pixels, myx, myy, &r, &g, &b, &a); + } /* Normalize */ r /= dots; @@ -513,160 +549,37 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, * as well as the most appropriate character to output. */ if(_caca_background == CACA_BACKGROUND_SOLID) { -# define XRATIO 5*5 -# define YRATIO 3*3 -# define HRATIO 2*2 -# define FUZZINESS XRATIO * 0x800 - - /* distance to black */ - distbg = XRATIO * val * val; - distbg += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - distbg = distbg * 2 / 4; - outbg = CACA_COLOR_BLACK; + unsigned char point; + int distfg, distbg; - /* distance to 30% */ - dist = XRATIO * (val - 0x600) * (val - 0x600) - + YRATIO * sat * sat; - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - dist = dist * 3 / 2; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = CACA_COLOR_DARKGRAY; - distbg = dist; - } - else - { - outfg = CACA_COLOR_DARKGRAY; - distfg = dist; - } + lookup_colors[4] = dark_colors[1 + hue / 0x1000]; + lookup_colors[5] = light_colors[1 + hue / 0x1000]; + lookup_colors[6] = dark_colors[hue / 0x1000]; + lookup_colors[7] = light_colors[hue / 0x1000]; - /* check dist to 70% */ - dist = XRATIO * (val - 0xa00) * (val - 0xa00) - + YRATIO * sat * sat; - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - dist = dist * 3 / 2; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = CACA_COLOR_LIGHTGRAY; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = CACA_COLOR_LIGHTGRAY; - distfg = dist; - } + point = lookup_table[val * LOOKUP_VAL / 0x1000] + [sat * LOOKUP_SAT / 0x1000] + [(hue & 0xfff) * LOOKUP_HUE / 0x1000]; - /* check dist to white */ - dist = XRATIO * (val - 0x1000) * (val - 0x1000) - + YRATIO * sat * sat; - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = CACA_COLOR_WHITE; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = CACA_COLOR_WHITE; - distfg = dist; - } - - hue1 = (hue + 0x800) & ~0xfff; - if(hue > hue1) - hue2 = (hue + 0x1800) & ~0xfff; - else - hue2 = (hue - 0x800) & ~0xfff; - - /* check dist to 2nd closest dark color */ - dist = XRATIO * (val - 0x600) * (val - 0x600) - + YRATIO * (sat - 0x1000) * (sat - 0x1000) - + HRATIO * (hue - hue2) * (hue - hue2); - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - //dist = dist * 3 / 4; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = dark_colors[hue2 / 0x1000]; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = dark_colors[hue2 / 0x1000]; - distfg = dist; - } - - /* check dist to 2nd closest light color */ - dist = XRATIO * (val - 0x1000) * (val - 0x1000) - + YRATIO * (sat - 0x1000) * (sat - 0x1000) - + HRATIO * (hue - hue2) * (hue - hue2); - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - //dist = dist * 3 / 4; - //dist = dist / 2; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = light_colors[hue2 / 0x1000]; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = light_colors[hue2 / 0x1000]; - distfg = dist; - } - - /* check dist to closest dark color */ - dist = XRATIO * (val - 0x600) * (val - 0x600) - + YRATIO * (sat - 0x1000) * (sat - 0x1000) - + HRATIO * (hue - hue1) * (hue - hue1); - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - dist = dist * 3 / 4; - if(dist <= distbg) - { - outfg = outbg; - distfg = distbg; - outbg = dark_colors[hue1 / 0x1000]; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = dark_colors[hue1 / 0x1000]; - distfg = dist; - } +try_again: + distfg = HSV_DISTANCE(hue % 0xfff, sat, val, (point >> 4)); + distbg = HSV_DISTANCE(hue % 0xfff, sat, val, (point & 0xf)); - /* check dist to closest light color */ - dist = XRATIO * (val - 0x1000) * (val - 0x1000) - + YRATIO * (sat - 0x1000) * (sat - 0x1000) - + HRATIO * (hue - hue1) * (hue - hue1); - dist += FUZZINESS * _get_dither() - 0x80 * FUZZINESS; - dist = dist / 2; - if(dist <= distbg) + /* Sanity check due to the lack of precision in lookup_table */ +#if 0 + if(distbg > distfg) { - outfg = outbg; - distfg = distbg; - outbg = light_colors[hue1 / 0x1000]; - distbg = dist; - } - else if(dist <= distfg) - { - outfg = light_colors[hue1 / 0x1000]; - distfg = dist; + point = ((point & 0xf) << 4) | (point >> 4); + goto try_again; } +#endif + if(distbg > distfg) + distbg = distfg; - if(distbg <= 0) distbg = 1; - if(distfg <= 0) distfg = 1; + outfg = lookup_colors[(point >> 4)]; + outbg = lookup_colors[(point & 0xf)]; - /* distbg can be > distfg because of dithering fuzziness */ - ch = distbg * 2 * (DENSITY_CHARS - 1) / (distbg + distfg); - ch = 4 * ch + _get_dither() / 0x40; - outch = density_chars[ch]; + outch = density_chars[4 * (distbg * (DENSITY_CHARS - 1) / distfg)]; } else { @@ -692,6 +605,164 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, } } +int _caca_init_bitmap(void) +{ + unsigned int v, s, h; + + /* These ones are constant */ + lookup_colors[0] = CACA_COLOR_BLACK; + lookup_colors[1] = CACA_COLOR_DARKGRAY; + lookup_colors[2] = CACA_COLOR_LIGHTGRAY; + lookup_colors[3] = CACA_COLOR_WHITE; + + /* These ones will be overwritten */ + lookup_colors[4] = CACA_COLOR_MAGENTA; + lookup_colors[5] = CACA_COLOR_LIGHTMAGENTA; + lookup_colors[6] = CACA_COLOR_RED; + lookup_colors[7] = CACA_COLOR_LIGHTRED; + + for(v = 0; v < LOOKUP_VAL; v++) + for(s = 0; s < LOOKUP_SAT; s++) + for(h = 0; h < LOOKUP_HUE; h++) + { + int distbg, distfg, dist; + int val, sat, hue; + unsigned char outbg, outfg; + + val = 0x1000 * v / LOOKUP_VAL; + sat = 0x1000 * s / LOOKUP_SAT; + hue = 0x1000 * h / LOOKUP_HUE; + + /* distance to black */ + distbg = HSV_DISTANCE(hue, sat, val, 0); + distbg = distbg * 2 / 4; + outbg = 0; + + /* distance to 30% */ + dist = HSV_DISTANCE(hue, sat, val, 1); + dist = dist * 3 / 2; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 1; + distbg = dist; + } + else + { + outfg = 1; + distfg = dist; + } + + /* check dist to 70% */ + dist = HSV_DISTANCE(hue, sat, val, 2); + dist = dist * 3 / 2; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 2; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 2; + distfg = dist; + } + + /* check dist to white */ + dist = HSV_DISTANCE(hue, sat, val, 3); + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 3; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 3; + distfg = dist; + } + + /* check dist to 2nd closest dark color */ + dist = HSV_DISTANCE(hue, sat, val, 4); + //dist = dist * 3 / 4; + dist = dist * 3 / 4; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 4; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 4; + distfg = dist; + } + + /* check dist to 2nd closest light color */ + dist = HSV_DISTANCE(hue, sat, val, 5); + dist = dist / 2; + //dist = dist * 3 / 4; + //dist = dist / 2; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 5; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 5; + distfg = dist; + } + + /* check dist to closest dark color */ + dist = HSV_DISTANCE(hue, sat, val, 6); + dist = dist * 3 / 4; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 6; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 6; + distfg = dist; + } + + /* check dist to closest light color */ + dist = HSV_DISTANCE(hue, sat, val, 7); + dist = dist / 2; + if(dist <= distbg) + { + outfg = outbg; + distfg = distbg; + outbg = 7; + distbg = dist; + } + else if(dist <= distfg) + { + outfg = 7; + distfg = dist; + } + + lookup_table[v][s][h] = (outfg << 4) | outbg; + } + + return 0; +} + +int _caca_end_bitmap(void) +{ + return 0; +} + /* * XXX: The following functions are local. */ diff --git a/src/caca.c b/src/caca.c index 95cc8a3..94f6666 100644 --- a/src/caca.c +++ b/src/caca.c @@ -155,6 +155,9 @@ int caca_init(void) if(_caca_init_graphics()) return -1; + if(_caca_init_bitmap()) + return -1; + return 0; } @@ -319,6 +322,7 @@ char const *caca_get_feature_name(enum caca_feature feature) */ void caca_end(void) { + _caca_end_bitmap(); _caca_end_graphics(); #if defined(USE_SLANG) diff --git a/src/caca_internals.h b/src/caca_internals.h index 6df5d91..640e36b 100644 --- a/src/caca_internals.h +++ b/src/caca_internals.h @@ -53,6 +53,8 @@ extern enum caca_driver _caca_driver; /* Initialisation functions */ extern int _caca_init_graphics(void); extern int _caca_end_graphics(void); +extern int _caca_init_bitmap(void); +extern int _caca_end_bitmap(void); /* Cached screen size */ extern unsigned int _caca_width;