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. --- src/palette.c | 32 ++++++++++++++++++++++++-- src/plot/8bpp.c | 2 +- src/plot/common.c | 69 +++++++++++++++++++++++++++++++++++++++---------------- src/surface/sdl.c | 2 +- 4 files changed, 81 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/palette.c b/src/palette.c index eba95cd..d600001 100644 --- a/src/palette.c +++ b/src/palette.c @@ -13,12 +13,13 @@ #include #include #include +#include #include "palette.h" /** 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) { *palette = malloc(sizeof(struct nsfb_palette_s)); if (*palette == NULL) { @@ -28,14 +29,41 @@ bool nsfb_palette_new(struct nsfb_palette_s **palette) (*palette)->type = NSFB_PALETTE_EMPTY; (*palette)->last = 0; + (*palette)->dither = false; + (*palette)->dither_ctx.data_len = width * 3; + (*palette)->dither_ctx.data = malloc(width * 3 * sizeof(int)); + if ((*palette)->dither_ctx.data == NULL) { + nsfb_palette_free(*palette); + return false; + } + return true; } /** Free a palette object. */ void nsfb_palette_free(struct nsfb_palette_s *palette) { - if (palette != NULL) + if (palette != NULL) { + if (palette->dither_ctx.data != NULL) { + free(palette->dither_ctx.data); + } free(palette); + } +} + +/** Init error diffusion for a plot. */ +void nsfb_palette_dither_init(struct nsfb_palette_s *palette, int width) +{ + palette->dither = true; + memset(palette->dither_ctx.data, 0, palette->dither_ctx.data_len); + palette->dither_ctx.width = width * 3; + palette->dither_ctx.current = 0; +} + +/** Finalise error diffusion after a plot. */ +void nsfb_palette_dither_fini(struct nsfb_palette_s *palette) +{ + palette->dither = false; } /** Generate libnsfb 8bpp default palette. */ diff --git a/src/plot/8bpp.c b/src/plot/8bpp.c index 05574d8..0245542 100644 --- a/src/plot/8bpp.c +++ b/src/plot/8bpp.c @@ -39,7 +39,7 @@ static uint8_t colour_to_pixel(nsfb_t *nsfb, nsfb_colour_t c) if (nsfb->palette == NULL) return 0; - return nsfb_palette_best_match(nsfb->palette, c); + return nsfb_palette_best_match_dither(nsfb->palette, c); } #define PLOT_TYPE uint8_t diff --git a/src/plot/common.c b/src/plot/common.c index 185e323..c9f9dc1 100644 --- a/src/plot/common.c +++ b/src/plot/common.c @@ -16,6 +16,8 @@ #error PLOT_LINELEN must be a macro to increment a line length #endif +#include "palette.h" + #define SIGN(x) ((x<0) ? -1 : ((x>0) ? 1 : 0)) static bool @@ -249,8 +251,7 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, nsfb_bbox_t clipped; /* clipped display */ /* The part of the scaled image actually displayed is cropped to the - * current context. - */ + * current context. */ clipped.x0 = x; clipped.y0 = y; clipped.x1 = x + width; @@ -271,6 +272,10 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, else rwidth = width; + if (nsfb->palette != NULL) { + nsfb_palette_dither_init(nsfb->palette, rwidth); + } + /* get veritcal (y) and horizontal (x) scale factors; both integer * part and remainder */ dx = bmp_width / width; @@ -299,7 +304,8 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, pvideo = get_xy_loc(nsfb, clipped.x0, clipped.y0); pvideo_limit = pvideo + PLOT_LINELEN(nsfb->linelen) * rheight; if (alpha) { - for (; pvideo < pvideo_limit; pvideo += PLOT_LINELEN(nsfb->linelen)) { + for (; pvideo < pvideo_limit; + pvideo += PLOT_LINELEN(nsfb->linelen)) { /* looping through render area vertically */ xoff = xoffs; rx = rxs; @@ -322,7 +328,8 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, xloop))); } /* plot pixel */ - *(pvideo + xloop) = colour_to_pixel(nsfb, abpixel); + *(pvideo + xloop) = colour_to_pixel( + nsfb, abpixel); } /* handle horizontal interpolation */ xoff += dx; @@ -341,7 +348,8 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, } } } else { - for (; pvideo < pvideo_limit; pvideo += PLOT_LINELEN(nsfb->linelen)) { + for (; pvideo < pvideo_limit; + pvideo += PLOT_LINELEN(nsfb->linelen)) { /* looping through render area vertically */ xoff = xoffs; rx = rxs; @@ -350,7 +358,8 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, /* get value of source pixel in question */ abpixel = pixel[yoff + xoff]; /* plot pixel */ - *(pvideo + xloop) = colour_to_pixel(nsfb, abpixel); + *(pvideo + xloop) = colour_to_pixel( + nsfb, abpixel); /* handle horizontal interpolation */ xoff += dx; @@ -369,6 +378,11 @@ static bool bitmap_scaled(nsfb_t *nsfb, const nsfb_bbox_t *loc, } } } + + if (nsfb->palette != NULL) { + nsfb_palette_dither_fini(nsfb->palette); + } + return true; } @@ -391,17 +405,16 @@ bitmap(nsfb_t *nsfb, int height = loc->y1 - loc->y0; nsfb_bbox_t clipped; /* clipped display */ - if (width == 0 || height == 0) - return true; + if (width == 0 || height == 0) + return true; /* Scaled bitmaps are handled by a separate function */ if (width != bmp_width || height != bmp_height) return bitmap_scaled(nsfb, loc, pixel, bmp_width, bmp_height, bmp_stride, alpha); - /* The part of the scaled image actually displayed is cropped to the - * current context. - */ + /* The part of the image actually displayed is cropped to the + * current context. */ clipped.x0 = x; clipped.y0 = y; clipped.x1 = x + width; @@ -416,6 +429,10 @@ bitmap(nsfb_t *nsfb, if (width > (clipped.x1 - clipped.x0)) width = (clipped.x1 - clipped.x0); + if (nsfb->palette != NULL) { + nsfb_palette_dither_init(nsfb->palette, width); + } + xoff = clipped.x0 - x; yoff = (clipped.y0 - y) * bmp_stride; height = height * bmp_stride + yoff; @@ -428,16 +445,22 @@ bitmap(nsfb_t *nsfb, for (xloop = 0; xloop < width; xloop++) { abpixel = pixel[yloop + xloop + xoff]; if ((abpixel & 0xFF000000) != 0) { - /* pixel is not transparent; have to - * plot something */ - if ((abpixel & 0xFF000000) != 0xFF000000) { - /* pixel is not opaque; need to - * blend */ - abpixel = nsfb_plot_ablend(abpixel, - pixel_to_colour(nsfb, *(pvideo + xloop))); + /* pixel is not transparent; have to + * plot something */ + if ((abpixel & 0xFF000000) != + 0xFF000000) { + /* pixel is not opaque; need to + * blend */ + abpixel = nsfb_plot_ablend( + abpixel, + pixel_to_colour( + nsfb, + *(pvideo + + xloop))); } - *(pvideo + xloop) = colour_to_pixel(nsfb, abpixel); + *(pvideo + xloop) = colour_to_pixel( + nsfb, abpixel); } } pvideo += PLOT_LINELEN(nsfb->linelen); @@ -446,11 +469,17 @@ bitmap(nsfb_t *nsfb, for (yloop = yoff; yloop < height; yloop += bmp_stride) { for (xloop = 0; xloop < width; xloop++) { abpixel = pixel[yloop + xloop + xoff]; - *(pvideo + xloop) = colour_to_pixel(nsfb, abpixel); + *(pvideo + xloop) = colour_to_pixel( + nsfb, abpixel); } pvideo += PLOT_LINELEN(nsfb->linelen); } } + + if (nsfb->palette != NULL) { + nsfb_palette_dither_fini(nsfb->palette); + } + return true; } diff --git a/src/surface/sdl.c b/src/surface/sdl.c index 7a86dc1..0554e26 100644 --- a/src/surface/sdl.c +++ b/src/surface/sdl.c @@ -458,7 +458,7 @@ static int sdl_initialise(nsfb_t *nsfb) nsfb->surface_priv = sdl_screen; if (nsfb->bpp == 8) { - nsfb_palette_new(&nsfb->palette); + nsfb_palette_new(&nsfb->palette, nsfb->width); set_palette(nsfb); } -- cgit v1.2.3