From 0804bb7e067e66d4b05a6c45f9736b1e20505b96 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 28 Sep 2012 11:19:34 +0100 Subject: Add error diffusion to palette based rendering. Only used for bitmap and scaled bitmap plots. Doesn't do serpentine path, since that would need changes to the common bitmap rendering code. --- include/palette.h | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/palette.h b/include/palette.h index 845c1bc..f975ef2 100644 --- a/include/palette.h +++ b/include/palette.h @@ -27,21 +27,35 @@ struct nsfb_palette_s { enum nsfb_palette_type_e type; /**< Palette type */ uint8_t last; /**< Last used palette index */ nsfb_colour_t data[256]; /**< Palette for index modes */ + + bool dither; /**< Whether to use error diffusion */ + struct { + int width; /**< Length of error value buffer ring*/ + int current; /**< Current pos in ring buffer*/ + int *data; /**< Ring buffer error values */ + int data_len; /**< Max size of ring */ + } dither_ctx; }; + /** Create an empty palette object. */ -bool nsfb_palette_new(struct nsfb_palette_s **palette); +bool nsfb_palette_new(struct nsfb_palette_s **palette, int width); /** Free a palette object. */ void nsfb_palette_free(struct nsfb_palette_s *palette); +/** Init error diffusion for a plot. */ +void nsfb_palette_dither_init(struct nsfb_palette_s *palette, int width); + +/** Finalise error diffusion after a plot. */ +void nsfb_palette_dither_fini(struct nsfb_palette_s *palette); + /** Generate libnsfb 8bpp default palette. */ void nsfb_palette_generate_nsfb_8bpp(struct nsfb_palette_s *palette); - -static inline uint8_t nsfb_palette_best_match( - const struct nsfb_palette_s *palette, - nsfb_colour_t c) +/** Find best palette match for given colour. */ +static inline uint8_t nsfb_palette_best_match(struct nsfb_palette_s *palette, + nsfb_colour_t c, int *r_error, int *g_error, int *b_error) { uint8_t best_col = 0; @@ -68,6 +82,9 @@ static inline uint8_t nsfb_palette_best_match( best_col = col; best_distance = cur_distance; + *r_error = dr; + *g_error = dg; + *b_error = db; /* Index into grayscale part */ col = (( c & 0xFF) + @@ -82,6 +99,9 @@ static inline uint8_t nsfb_palette_best_match( if (cur_distance < best_distance) { best_distance = cur_distance; best_col = col; + *r_error = dr; + *g_error = dg; + *b_error = db; } break; @@ -97,6 +117,9 @@ static inline uint8_t nsfb_palette_best_match( if (cur_distance < best_distance) { best_distance = cur_distance; best_col = col; + *r_error = dr; + *g_error = dg; + *b_error = db; } } break; @@ -108,4 +131,101 @@ static inline uint8_t nsfb_palette_best_match( return best_col; } +/** Find best palette match for given colour, with error diffusion. */ +static inline uint8_t nsfb_palette_best_match_dither( + struct nsfb_palette_s *palette, nsfb_colour_t c) +{ + int r, g, b; + int current; + int error; + int width = palette->dither_ctx.width; + uint8_t best_col_index; + + if (palette == NULL) + return 0; + + if (palette->dither == false) + return nsfb_palette_best_match(palette, c, &r, &g, &b); + + current = palette->dither_ctx.current; + + /* Get RGB components of colour, and apply error */ + r = ( c & 0xFF) + palette->dither_ctx.data[current ]; + g = ((c >> 8) & 0xFF) + palette->dither_ctx.data[current + 1]; + b = ((c >> 16) & 0xFF) + palette->dither_ctx.data[current + 2]; + + /* Clamp new RGB components to range */ + if (r < 0) r = 0; + if (r > 255) r = 255; + if (g < 0) g = 0; + if (g > 255) g = 255; + if (b < 0) b = 0; + if (b > 255) b = 255; + + /* Reset error diffusion slots to 0 */ + palette->dither_ctx.data[current ] = 0; + palette->dither_ctx.data[current + 1] = 0; + palette->dither_ctx.data[current + 2] = 0; + + /* Rebuild colour from modified components */ + c = r + (g << 8) + (b << 16); + + /* Get best match for pixel, and find errors for each component */ + best_col_index = nsfb_palette_best_match(palette, c, &r, &g, &b); + + /* Advance one set of error diffusion slots */ + current += 3; + if (current >= width) + current = 0; + palette->dither_ctx.current = current; + + /* Save errors + * + * [*]-[N] + * / | \ + * [l]-[m]-[r] + */ + error = current; + + /* Error for [N] (next) */ + if (error != 0) { + /* The pixel exists */ + palette->dither_ctx.data[error ] += r * 7 / 16; + palette->dither_ctx.data[error + 1] += g * 7 / 16; + palette->dither_ctx.data[error + 2] += b * 7 / 16; + } + + error += width - 2 * 3; + if (error >= width) + error -= width; + /* Error for [l] (below, left) */ + if (error >= 0 && error != 3) { + /* The pixel exists */ + palette->dither_ctx.data[error ] += r * 3 / 16; + palette->dither_ctx.data[error + 1] += g * 3 / 16; + palette->dither_ctx.data[error + 2] += b * 3 / 16; + } + + error += 3; + if (error >= width) + error -= width; + /* Error for [m] (below, middle) */ + palette->dither_ctx.data[error ] += r * 5 / 16; + palette->dither_ctx.data[error + 1] += g * 5 / 16; + palette->dither_ctx.data[error + 2] += b * 5 / 16; + + error += 3; + if (error >= width) + error -= width; + /* Error for [r] (below, right) */ + if (error != 0) { + /* The pixel exists */ + palette->dither_ctx.data[error ] += r / 16; + palette->dither_ctx.data[error + 1] += g / 16; + palette->dither_ctx.data[error + 2] += b / 16; + } + + return best_col_index; +} + #endif /* PALETTE_H */ -- cgit v1.2.3