summaryrefslogtreecommitdiff
path: root/include/palette.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/palette.h')
-rw-r--r--include/palette.h130
1 files changed, 125 insertions, 5 deletions
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 */