From 0bb7164b917b39c1b2f24c9e86525ae6c3ca310d Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Sat, 5 Apr 2003 21:17:51 +0000 Subject: [PATCH] worked on Error diffusion dithering algorithm, but it doesn't end up looking much better than OrderedDither --- stepmania/src/RageBitmapTexture.cpp | 1 + stepmania/src/SDL_dither.cpp | 75 +++++++++++++++++++++++++++++ stepmania/src/SDL_dither.h | 1 + stepmania/src/SDL_utils.cpp | 37 ++++++++++++++ stepmania/src/SDL_utils.h | 4 ++ 5 files changed, 118 insertions(+) diff --git a/stepmania/src/RageBitmapTexture.cpp b/stepmania/src/RageBitmapTexture.cpp index dc96dcd895..faf6a52d24 100644 --- a/stepmania/src/RageBitmapTexture.cpp +++ b/stepmania/src/RageBitmapTexture.cpp @@ -352,6 +352,7 @@ retry: PixFmtMasks[pixfmt].masks[2], PixFmtMasks[pixfmt].masks[3]); SM_SDL_OrderedDither(img, dst); + //SM_SDL_ErrorDiffusionDither(img, dst); SDL_FreeSurface(img); img = dst; } diff --git a/stepmania/src/SDL_dither.cpp b/stepmania/src/SDL_dither.cpp index 2048a52eb2..71ff70befa 100644 --- a/stepmania/src/SDL_dither.cpp +++ b/stepmania/src/SDL_dither.cpp @@ -6,6 +6,13 @@ #define DitherMatDim 4 + +/* + Added error-diffusion algorithm. (SM_SDL_ErrorDiffusionDither) + Error distributed per-row, left to right. + http://www.gamasutra.com/features/19990521/pixel_conversion_03.htm + */ + /* Fractions, 0/16 to 15/16: */ static const int DitherMat[DitherMatDim][DitherMatDim] = { @@ -122,3 +129,71 @@ void SM_SDL_OrderedDither(const SDL_Surface *src, SDL_Surface *dst) } } } + + +#define CLAMP(x, l, h) {if (x > h) x = h; else if (x < l) x = l;} + +void SM_SDL_ErrorDiffusionDither(const SDL_Surface *src, SDL_Surface *dst) +{ + /* We can't dither to paletted surfaces. */ + ASSERT(dst->format->BytesPerPixel > 1); + + /* For each row: */ + for(int row = 0; row < src->h; ++row) + { + Sint32 accumError[4] = { 0, 0, 0, 0 }; // accum error values are reset every row + + const Uint8 *srcp = (const Uint8 *)src->pixels + row * src->pitch; + Uint8 *dstp = (Uint8 *)dst->pixels + row * dst->pitch; + + /* For each pixel in row: */ + for(int col = 0; col < src->w; ++col) + { + Uint8 originalColors[4]; + mySDL_GetRGBAV(srcp, src, originalColors); + + Uint8 colorsPlusError[4]; + int c; + for(c = 0; c < 4; ++c) + { + // move some error to the new pixel (without overflowing) + Sint32 errorToAdd; + if( accumError[c] >= 0 ) + errorToAdd = min( accumError[c], (Sint32)255 - originalColors[c] ); + else + errorToAdd = max( accumError[c], (Sint32)-originalColors[c] ); + + colorsPlusError[c] = (Uint8)(originalColors[c] + errorToAdd); + accumError[c] -= errorToAdd; + + // make sure we didn't overflow + ASSERT( (Uint8)(originalColors[c] + errorToAdd) == (Sint32)(originalColors[c] + errorToAdd) ); + + } + mySDL_SetRGBAV(dstp, dst, colorsPlusError); + + + Uint8 ditheredColors[4]; + mySDL_GetRGBAV(dstp, dst, ditheredColors); + + for(c = 0; c < 4; ++c) + accumError[c] += originalColors[c] - ditheredColors[c]; + + /* Blank the alpha accumulated error. + * This has the effect of not dithering the alpha channel. */ + accumError[3] = 0; + + for(c = 0; c < 4; ++c) + { + // Reduce funky streaks in low-bit channels by clamping error. + CLAMP( accumError[c], -128, +128 ); + + // Keep only a fraction of the error to make the effect more subtle. + accumError[c] /= 4; + } + + srcp += src->format->BytesPerPixel; + dstp += dst->format->BytesPerPixel; + } + } +} diff --git a/stepmania/src/SDL_dither.h b/stepmania/src/SDL_dither.h index 82243904cc..65fa27a980 100644 --- a/stepmania/src/SDL_dither.h +++ b/stepmania/src/SDL_dither.h @@ -2,5 +2,6 @@ #define SM_SDL_DITHER_H 1 void SM_SDL_OrderedDither(const SDL_Surface *src, SDL_Surface *dst); +void SM_SDL_ErrorDiffusionDither(const SDL_Surface *src, SDL_Surface *dst); #endif diff --git a/stepmania/src/SDL_utils.cpp b/stepmania/src/SDL_utils.cpp index fdb69165c9..778f925fc2 100644 --- a/stepmania/src/SDL_utils.cpp +++ b/stepmania/src/SDL_utils.cpp @@ -107,6 +107,27 @@ void mySDL_GetRawRGBAV(const Uint8 *p, const SDL_Surface *src, Uint8 *v) mySDL_GetRawRGBAV(pixel, src, v); } +void mySDL_GetRGBAV(Uint32 pixel, const SDL_Surface *src, Uint8 *v) +{ + mySDL_GetRawRGBAV(pixel, src, v); + const SDL_PixelFormat *fmt = src->format; + v[0] = v[0] << fmt->Rloss; + v[1] = v[1] << fmt->Gloss; + v[2] = v[2] << fmt->Bloss; + // Correct for surfaces that don't have an alpha channel. + if( fmt->Aloss == 8) + v[3] = 255; + else + v[3] = v[3] << fmt->Aloss; +} + +void mySDL_GetRGBAV(const Uint8 *p, const SDL_Surface *src, Uint8 *v) +{ + Uint32 pixel = decodepixel(p, src->format->BytesPerPixel); + mySDL_GetRGBAV(pixel, src, v); +} + + /* Inverse of mySDL_GetRawRGBAV. */ Uint32 mySDL_SetRawRGBAV(const SDL_PixelFormat *fmt, const Uint8 *v) { @@ -122,6 +143,22 @@ void mySDL_SetRawRGBAV(Uint8 *p, const SDL_Surface *src, const Uint8 *v) encodepixel(p, src->format->BytesPerPixel, pixel); } +/* Inverse of mySDL_GetRGBAV. */ +Uint32 mySDL_SetRGBAV(const SDL_PixelFormat *fmt, const Uint8 *v) +{ + return (v[0] >> fmt->Rloss) << fmt->Rshift | + (v[1] >> fmt->Gloss) << fmt->Gshift | + (v[2] >> fmt->Bloss) << fmt->Bshift | + (v[3] >> fmt->Aloss) << fmt->Ashift; +} + +void mySDL_SetRGBAV(Uint8 *p, const SDL_Surface *src, const Uint8 *v) +{ + Uint32 pixel = mySDL_SetRGBAV(src->format, v); + encodepixel(p, src->format->BytesPerPixel, pixel); +} + + /* Get the number of bits representing each color channel in fmt. */ void mySDL_GetBitsPerChannel(const SDL_PixelFormat *fmt, Uint32 bits[4]) { diff --git a/stepmania/src/SDL_utils.h b/stepmania/src/SDL_utils.h index 56c25eb512..f0283cd71d 100644 --- a/stepmania/src/SDL_utils.h +++ b/stepmania/src/SDL_utils.h @@ -14,9 +14,13 @@ void encodepixel(Uint8 *p, int bpp, Uint32 pixel); void mySDL_GetRawRGBAV(Uint32 pixel, const SDL_Surface *src, Uint8 *v); void mySDL_GetRawRGBAV(const Uint8 *p, const SDL_Surface *src, Uint8 *v); +void mySDL_GetRGBAV(Uint32 pixel, const SDL_Surface *src, Uint8 *v); +void mySDL_GetRGBAV(const Uint8 *p, const SDL_Surface *src, Uint8 *v); Uint32 mySDL_SetRawRGBAV(const SDL_PixelFormat *fmt, const Uint8 *v); void mySDL_SetRawRGBAV(Uint8 *p, const SDL_Surface *src, const Uint8 *v); +Uint32 mySDL_SetRGBAV(const SDL_PixelFormat *fmt, const Uint8 *v); +void mySDL_SetRGBAV(Uint8 *p, const SDL_Surface *src, const Uint8 *v); void mySDL_GetBitsPerChannel(const SDL_PixelFormat *fmt, Uint32 bits[4]); void ConvertSDLSurface(SDL_Surface *&image,