Browse Source

* src/bitmap.c:

+ 7-3-5-1 Floyd-Steinberg dithering. Breaks all other ditherers.
tags/v0.99.beta14
Sam Hocevar sam 20 years ago
parent
commit
e51f8e348c
1 changed files with 139 additions and 59 deletions
  1. +139
    -59
      src/bitmap.c

+ 139
- 59
src/bitmap.c View File

@@ -41,7 +41,10 @@ typedef unsigned int uint32_t;
# include <endian.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#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)


Loading…
Cancel
Save