From e51f8e348ca61d48ed055daade8c98b0010b731c Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Thu, 4 Nov 2004 21:18:52 +0000 Subject: [PATCH] * src/bitmap.c: + 7-3-5-1 Floyd-Steinberg dithering. Breaks all other ditherers. --- src/bitmap.c | 198 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 139 insertions(+), 59 deletions(-) diff --git a/src/bitmap.c b/src/bitmap.c index ef80e40..0ddf97a 100644 --- a/src/bitmap.c +++ b/src/bitmap.c @@ -41,7 +41,10 @@ typedef unsigned int uint32_t; # include #endif +#include #include +#include +#include #include "caca.h" #include "caca_internals.h" @@ -79,6 +82,27 @@ static int const hsv_palette[] = 2, 0x0, 0xfff, 0xfff /* light red */ }; +/* RGB palette for the new colour picker */ +static int rgb_palette[] = +{ + 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7ff, + 0x0, 0x7ff, 0x0, + 0x0, 0x7ff, 0x7ff, + 0x7ff, 0x0, 0x0, + 0x7ff, 0x0, 0x7ff, + 0x7ff, 0x7ff, 0x0, + 0x7ff, 0x7ff, 0x7ff, + 0x3ff, 0x3ff, 0x3ff, + 0x000, 0x000, 0xfff, + 0x000, 0xfff, 0x000, + 0x000, 0xfff, 0xfff, + 0xfff, 0x000, 0x000, + 0xfff, 0x000, 0xfff, + 0xfff, 0xfff, 0x000, + 0xfff, 0xfff, 0xfff, +}; + #if !defined(_DOXYGEN_SKIP_ME) #define HSV_XRATIO 6 #define HSV_YRATIO 3 @@ -107,6 +131,7 @@ static void get_rgba_default(struct caca_bitmap const *, uint8_t *, int, int, unsigned int *, unsigned int *, unsigned int *, unsigned int *); static inline void rgb2hsv_default(int, int, int, int *, int *, int *); +static inline int sq(int); /* Dithering methods */ static void init_no_dither(int); @@ -384,6 +409,11 @@ static inline void rgb2hsv_default(int r, int g, int b, } } +static inline int sq(int x) +{ + return x * x; +} + /** * \brief Draw a bitmap on the screen. * @@ -405,6 +435,9 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, unsigned int (*_get_dither) (void); void (*_increment_dither) (void); + int *floyd_steinberg, *fs_r, *fs_g, *fs_b; + int fs_length; + /* Only used when background is black */ static int const white_colors[] = { @@ -438,19 +471,17 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, /* FIXME: choose better characters! */ #if !defined(_DOXYGEN_SKIP_ME) -# define DENSITY_CHARS ((sizeof(density_chars)/sizeof(char const)/4)-1) +# define DCHMAX ((sizeof(density_chars)/sizeof(char const)/4)-1) #endif static char const density_chars[] = " " - ". " - ".. " "...." "::::" ";=;=" "tftf" "%$%$" - "&KSZ" - "WXGM" + "SK&Z" + "XWGM" "@@@@" "8888" "####" @@ -515,16 +546,28 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, return; } + fs_length = ((int)_caca_width <= x2 ? (int)_caca_width : x2) + 1; + floyd_steinberg = malloc(3 * (fs_length + 2) * sizeof(int)); + memset(floyd_steinberg, 0, 3 * (fs_length + 2) * sizeof(int)); + fs_r = floyd_steinberg + 1; + fs_g = fs_r + fs_length + 2; + fs_b = fs_g + fs_length + 2; + for(y = y1 > 0 ? y1 : 0; y <= y2 && y <= (int)_caca_height; y++) + { + int remain_r = 0, remain_g = 0, remain_b = 0; + int remain_r_next = 0, remain_g_next = 0, remain_b_next = 0; + for(x = x1 > 0 ? x1 : 0, _init_dither(y); x <= x2 && x <= (int)_caca_width; x++) { - int ch; - unsigned int r, g, b, a; - int hue, sat, val; - int fromx, fromy, tox, toy, myx, myy, dots; - enum caca_color outfg, outbg; + unsigned int i; + int ch = 0, distmin; + int r, g, b, a, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b; + int fromx, fromy, tox, toy, myx, myy, dots, dist; + + enum caca_color outfg = 0, outbg = 0; char outch; r = g = b = a = 0; @@ -573,61 +616,94 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, } if(bitmap->has_alpha && a < 0x800) + { + remain_r = remain_g = remain_b = 0; + fs_r[x] = 0; + fs_g[x] = 0; + fs_b[x] = 0; continue; + } - /* Now get HSV from RGB */ - rgb2hsv_default(r, g, b, &hue, &sat, &val); - - /* The hard work: calculate foreground and background colours, - * as well as the most appropriate character to output. */ - if(_caca_background == CACA_BACKGROUND_SOLID) + r += remain_r; + g += remain_g; + b += remain_b; + r += remain_r_next; + g += remain_g_next; + b += remain_b_next; + remain_r_next = fs_r[x+1]; + remain_g_next = fs_g[x+1]; + remain_b_next = fs_b[x+1]; + + distmin = INT_MAX; + for(i = 0; i < 16; i++) { - unsigned char point; - int distfg, distbg; - - 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]; - - point = hsv_distances[(val + _get_dither() * (0x1000 / LOOKUP_VAL) - / 0x100) * (LOOKUP_VAL - 1) / 0x1000] - [(sat + _get_dither() * (0x1000 / LOOKUP_SAT) - / 0x100) * (LOOKUP_SAT - 1) / 0x1000] - [((hue & 0xfff) + _get_dither() - * (0x1000 / LOOKUP_HUE) / 0x100) - * (LOOKUP_HUE - 1) / 0x1000]; - - distfg = HSV_DISTANCE(hue % 0xfff, sat, val, (point >> 4)); - distbg = HSV_DISTANCE(hue % 0xfff, sat, val, (point & 0xf)); - - /* Sanity check due to the lack of precision in hsv_distances, - * and distbg can be > distfg because of dithering fuzziness. */ - if(distbg > distfg) - distbg = distfg; - - outfg = lookup_colors[(point >> 4)]; - outbg = lookup_colors[(point & 0xf)]; - - ch = distbg * 2 * (DENSITY_CHARS - 1) / (distbg + distfg); - ch = 4 * ch + _get_dither() / 0x40; - outch = density_chars[ch]; + dist = sq(r - rgb_palette[i * 3]) + + sq(g - rgb_palette[i * 3 + 1]) + + sq(b - rgb_palette[i * 3 + 2]); + if(dist < distmin) + { + outbg = i; + distmin = dist; + } } - else - { - outbg = CACA_COLOR_BLACK; - if((unsigned int)sat < 0x200 + _get_dither() * 0x8) - outfg = white_colors[1 + (val * 2 + _get_dither() * 0x10) - / 0x1000]; - else if((unsigned int)val > 0x800 + _get_dither() * 0x4) - outfg = light_colors[(hue + _get_dither() * 0x10) / 0x1000]; - else - outfg = dark_colors[(hue + _get_dither() * 0x10) / 0x1000]; + bg_r = rgb_palette[outbg * 3]; + bg_g = rgb_palette[outbg * 3 + 1]; + bg_b = rgb_palette[outbg * 3 + 2]; - ch = (val + 0x2 * _get_dither()) * 10 / 0x1000; - ch = 4 * ch + _get_dither() / 0x40; - outch = density_chars[ch]; + distmin = INT_MAX; + for(i = 0; i < 16; i++) + { + if(i == outbg) + continue; + dist = sq(r - rgb_palette[i * 3]) + + sq(g - rgb_palette[i * 3 + 1]) + + sq(b - rgb_palette[i * 3 + 2]); + if(dist < distmin) + { + outfg = i; + distmin = dist; + } } + fg_r = rgb_palette[outfg * 3]; + fg_g = rgb_palette[outfg * 3 + 1]; + fg_b = rgb_palette[outfg * 3 + 2]; + + distmin = INT_MAX; + for(i = 0; i < DCHMAX - 1; i++) + { + int newr = i * fg_r + ((2*DCHMAX-1) - i) * bg_r; + int newg = i * fg_g + ((2*DCHMAX-1) - i) * bg_g; + int newb = i * fg_b + ((2*DCHMAX-1) - i) * bg_b; + dist = abs(r * (2*DCHMAX-1) - newr) + * abs(g * (2*DCHMAX-1) - newg) + * abs(b * (2*DCHMAX-1) - newb); + + if(dist < distmin) + { + ch = i; + distmin = dist; + } + } + outch = density_chars[4 * ch]; + + remain_r = r - (fg_r * ch + bg_r * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1); + remain_g = g - (fg_g * ch + bg_g * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1); + remain_b = b - (fg_b * ch + bg_b * ((2*DCHMAX-1) - ch)) / (2*DCHMAX-1); + remain_r_next = fs_r[x+1]; + remain_g_next = fs_g[x+1]; + remain_b_next = fs_b[x+1]; + fs_r[x-1] += 3 * remain_r / 16; + fs_g[x-1] += 3 * remain_g / 16; + fs_b[x-1] += 3 * remain_b / 16; + fs_r[x] = 5 * remain_r / 16; + fs_g[x] = 5 * remain_g / 16; + fs_b[x] = 5 * remain_b / 16; + fs_r[x+1] = 1 * remain_r / 16; + fs_g[x+1] = 1 * remain_g / 16; + fs_b[x+1] = 1 * remain_b / 16; + remain_r = 7 * remain_r / 16; + remain_g = 7 * remain_g / 16; + remain_b = 7 * remain_b / 16; /* Now output the character */ caca_set_color(outfg, outbg); @@ -635,6 +711,10 @@ void caca_draw_bitmap(int x1, int y1, int x2, int y2, _increment_dither(); } + /* end loop */ + } + + free(floyd_steinberg); } #if !defined(_DOXYGEN_SKIP_ME)