diff --git a/stepmania/src/RageSurface.cpp b/stepmania/src/RageSurface.cpp new file mode 100644 index 0000000000..7deade915e --- /dev/null +++ b/stepmania/src/RageSurface.cpp @@ -0,0 +1,225 @@ +#include "global.h" +#include "RageSurface.h" + +bool RageSurfaceColor::operator== ( const RageSurfaceColor &rhs ) const +{ + return rhs.r == r && rhs.g == g && rhs.b == b && rhs.a == a; +} + +int32_t RageSurfacePalette::FindColor( const RageSurfaceColor &color ) const +{ + for( int i = 0; i < ncolors; ++i ) + if( colors[i] == color ) + return i; + return -1; +} + +/* XXX: untested */ +int32_t RageSurfacePalette::FindClosestColor( const RageSurfaceColor &color ) const +{ + int iBest = -1; + int iBestDist = 99999; + for( int i = 0; i < ncolors; ++i ) + { + if( colors[i] == color ) + return i; + + int iDist = abs( colors[i].r - color.r ) + + abs( colors[i].g - color.g ) + + abs( colors[i].b - color.b ) + + abs( colors[i].a - color.a ); + if( iDist < iBestDist ) + { + iBestDist = iDist ; + iBest = i; + } + } + + return iBest; +} + +void RageSurfaceFormat::GetRGB( uint32_t val, uint8_t *r, uint8_t *g, uint8_t *b ) const +{ + if( BytesPerPixel == 1 ) + { + ASSERT( palette ); + *r = palette->colors[val].r; + *g = palette->colors[val].g; + *b = palette->colors[val].b; + } else { + *r = int8_t( (val & Rmask) >> Rshift << Rloss ); + *g = int8_t( (val & Gmask) >> Gshift << Gloss ); + *b = int8_t( (val & Bmask) >> Bshift << Bloss ); +// *a = int8_t( (val & Amask) >> Ashift << Aloss ); + } +} + +bool RageSurfaceFormat::MapRGBA( uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint32_t &val ) const +{ + if( BytesPerPixel == 1 ) + { + RageSurfaceColor c( r, g, b, a ); + int32_t n = palette->FindColor( c ); + if( n == -1 ) + return false; + val = (uint32_t) n; + } else { + val = (r >> Rloss << Rshift) | + (g >> Gloss << Gshift) | + (b >> Bloss << Bshift) | + (a >> Aloss << Ashift); + } + return true; +} + +bool RageSurfaceFormat::operator== ( const RageSurfaceFormat &rhs ) const +{ +#define COMP(a) if( a != rhs.a ) return false; + COMP( BytesPerPixel ); + COMP( Rmask ); + COMP( Gmask ); + COMP( Bmask ); + COMP( Amask ); + if( BytesPerPixel == 1 ) + COMP( palette ); + + return true; +} + +RageSurface::RageSurface() +{ + format = &fmt; + pixels = NULL; +} + +RageSurface::~RageSurface() +{ + delete [] pixels; +} + +static int GetShiftFromMask( uint32_t mask ) +{ + if( !mask ) + return 0; + + int iShift = 0; + while( (mask & 1) == 0 ) + { + mask >>= 1; + ++iShift; + } + return iShift; +} + +static int GetBitsFromMask( uint32_t mask ) +{ + if( !mask ) + return 0; + + mask >>= GetShiftFromMask(mask); + + int iBits = 0; + while( (mask & 1) == 1 ) + { + mask >>= 1; + ++iBits; + } + return iBits; +} + + +void SetupFormat( RageSurfaceFormat &fmt, + int width, int height, int BitsPerPixel, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask ) +{ + fmt.BitsPerPixel = BitsPerPixel; + fmt.BytesPerPixel = BitsPerPixel/8; + if( fmt.BytesPerPixel == 1 ) + { + fmt.Rmask = fmt.Gmask = fmt.Bmask = fmt.Amask = 0; + fmt.Rshift = fmt.Gshift = fmt.Bshift = fmt.Ashift = 0; + + /* Loss for paletted textures is zero, since the actual palette entries are 8-bit. */ + fmt.Rloss = fmt.Gloss = fmt.Bloss = fmt.Aloss = 0; + + fmt.palette = new RageSurfacePalette; + fmt.palette->ncolors = 256; + } + else + { + fmt.Rmask = Rmask; + fmt.Gmask = Gmask; + fmt.Bmask = Bmask; + fmt.Amask = Amask; + + fmt.Rshift = GetShiftFromMask( Rmask ); + fmt.Gshift = GetShiftFromMask( Gmask ); + fmt.Bshift = GetShiftFromMask( Bmask ); + fmt.Ashift = GetShiftFromMask( Amask ); + + fmt.Rloss = (uint8_t) (8-GetBitsFromMask( Rmask )); + fmt.Gloss = (uint8_t) (8-GetBitsFromMask( Gmask )); + fmt.Bloss = (uint8_t) (8-GetBitsFromMask( Bmask )); + fmt.Aloss = (uint8_t) (8-GetBitsFromMask( Amask )); + } +} + +RageSurface *CreateSurface( int width, int height, int BitsPerPixel, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask ) +{ + RageSurface *pImg = new RageSurface; + + SetupFormat( pImg->fmt, width, height, BitsPerPixel, Rmask, Gmask, Bmask, Amask ); + + pImg->w = width; + pImg->h = height; + pImg->flags = 0; + pImg->pitch = width*BitsPerPixel/8; + pImg->pixels = new uint8_t[ pImg->pitch*height ]; + +// if( BitsPerPixel == 8 ) +// { +// pImg->fmt.palette = new RageSurfacePalette; +// } + + return pImg; +} + +RageSurface *CreateSurfaceFrom( int width, int height, int BitsPerPixel, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask, uint8_t *pPixels, uint32_t pitch ) +{ + RageSurface *pImg = new RageSurface; + + SetupFormat( pImg->fmt, width, height, BitsPerPixel, Rmask, Gmask, Bmask, Amask ); + + pImg->w = width; + pImg->h = height; + pImg->flags = 0; + pImg->pitch = pitch; + pImg->pixels = pPixels; + + return pImg; +} + +/* + * (c) 2001-2004 Glenn Maynard, Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + diff --git a/stepmania/src/RageSurface.h b/stepmania/src/RageSurface.h new file mode 100644 index 0000000000..34d18a94fa --- /dev/null +++ b/stepmania/src/RageSurface.h @@ -0,0 +1,90 @@ +/* RageSurface - holds a simple 2d graphic surface */ + +#ifndef RAGE_SURFACE_H +#define RAGE_SURFACE_H + +/* XXX remove? */ +struct RageSurfaceColor +{ + uint8_t r, g, b, a; + RageSurfaceColor() { } + RageSurfaceColor( uint8_t r_, uint8_t g_, uint8_t b_, uint8_t a_ ): + r(r_), g(g_), b(b_), a(a_) { } + + bool operator== ( const RageSurfaceColor &rhs ) const; +}; + +struct RageSurfacePalette +{ + RageSurfaceColor colors[256]; + int32_t ncolors; + + /* Find the exact color; returns -1 if not found. */ + int32_t FindColor( const RageSurfaceColor &color ) const; + int32_t FindClosestColor( const RageSurfaceColor &color ) const; +}; + +struct RageSurfaceFormat +{ + int32_t BytesPerPixel; + int32_t BitsPerPixel; + uint32_t Rmask, Gmask, Bmask, Amask; + uint32_t Rshift, Gshift, Bshift, Ashift; + uint8_t Rloss, Gloss, Bloss, Aloss; + RageSurfacePalette *palette; + + void GetRGB( uint32_t val, uint8_t *r, uint8_t *g, uint8_t *b ) const; + + /* Return the decoded value for the given color; the result can be compared to + * decodepixel() results. If the image is paletted and the color isn't found, + * val is undefined and false is returned. */ + bool MapRGBA( uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint32_t &val ) const; + + /* MapRGBA, but also do a nearest-match on palette colors. */ + uint32_t MapNearestRGBA( uint8_t r, uint8_t g, uint8_t b, uint8_t a ) const; + + bool operator== ( const RageSurfaceFormat &rhs ) const; +}; + +struct RageSurface +{ + RageSurfaceFormat *format; /* compatibility only */ + RageSurfaceFormat fmt; + + uint8_t *pixels; + int32_t w, h, pitch; + int32_t flags; + + RageSurface(); + ~RageSurface(); +}; + +RageSurface *CreateSurface( int width, int height, int bpp, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask ); +RageSurface *CreateSurfaceFrom( int width, int height, int bpp, uint32_t Rmask, uint32_t Gmask, uint32_t Bmask, uint32_t Amask, uint8_t *pPixels, uint32_t pitch ); + +#endif + +/* + * (c) 2001-2004 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/RageSurfaceUtils.cpp b/stepmania/src/RageSurfaceUtils.cpp new file mode 100644 index 0000000000..bb64a26c6c --- /dev/null +++ b/stepmania/src/RageSurfaceUtils.cpp @@ -0,0 +1,994 @@ +#include "global.h" +#include "RageSurfaceUtils.h" +#include "RageSurface.h" +#include "RageUtil.h" +#include "RageFile.h" + +uint32_t RageSurfaceUtils::decodepixel( const uint8_t *p, int bpp ) +{ + switch(bpp) + { + case 1: return *p; + case 2: return *(uint16_t *)p; + case 3: + if( BYTE_ORDER == BIG_ENDIAN ) + return p[0] << 16 | p[1] << 8 | p[2]; + else + return p[0] | p[1] << 8 | p[2] << 16; + + case 4: return *(uint32_t *)p; + default: return 0; /* shouldn't happen, but avoids warnings */ + } +} + +void RageSurfaceUtils::encodepixel( uint8_t *p, int bpp, uint32_t pixel ) +{ + switch(bpp) + { + case 1: *p = uint8_t(pixel); break; + case 2: *(uint16_t *)p = uint16_t(pixel); break; + case 3: + if( BYTE_ORDER == BIG_ENDIAN ) + { + p[0] = uint8_t((pixel >> 16) & 0xff); + p[1] = uint8_t((pixel >> 8) & 0xff); + p[2] = uint8_t(pixel & 0xff); + } else { + p[0] = uint8_t(pixel & 0xff); + p[1] = uint8_t((pixel >> 8) & 0xff); + p[2] = uint8_t((pixel >> 16) & 0xff); + } + break; + case 4: *(uint32_t *)p = pixel; break; + } +} + +/* Get and set colors without scaling to 0..255. */ +void RageSurfaceUtils::GetRawRGBAV( uint32_t pixel, const RageSurfaceFormat &fmt, uint8_t *v ) +{ + if( fmt.BytesPerPixel == 1 ) + { + v[0] = fmt.palette->colors[pixel].r; + v[1] = fmt.palette->colors[pixel].g; + v[2] = fmt.palette->colors[pixel].b; + v[3] = fmt.palette->colors[pixel].a; + } else { + v[0] = uint8_t((pixel & fmt.Rmask) >> fmt.Rshift); + v[1] = uint8_t((pixel & fmt.Gmask) >> fmt.Gshift); + v[2] = uint8_t((pixel & fmt.Bmask) >> fmt.Bshift); + v[3] = uint8_t((pixel & fmt.Amask) >> fmt.Ashift); + } +} + +void RageSurfaceUtils::GetRawRGBAV( const uint8_t *p, const RageSurfaceFormat &fmt, uint8_t *v ) +{ + uint32_t pixel = decodepixel( p, fmt.BytesPerPixel ); + GetRawRGBAV( pixel, fmt, v ); +} + +void RageSurfaceUtils::GetRGBAV( uint32_t pixel, const RageSurface *src, uint8_t *v ) +{ + GetRawRGBAV(pixel, src->fmt, v); + const RageSurfaceFormat *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 RageSurfaceUtils::GetRGBAV( const uint8_t *p, const RageSurface *src, uint8_t *v ) +{ + uint32_t pixel = decodepixel(p, src->format->BytesPerPixel); + if( src->format->BytesPerPixel == 1 ) // paletted + { + memcpy( v, &src->format->palette->colors[pixel], sizeof(RageSurfaceColor)); + } + else // RGBA + GetRGBAV(pixel, src, v); +} + + +/* Inverse of GetRawRGBAV. */ +uint32_t RageSurfaceUtils::SetRawRGBAV( const RageSurfaceFormat *fmt, const uint8_t *v ) +{ + return v[0] << fmt->Rshift | + v[1] << fmt->Gshift | + v[2] << fmt->Bshift | + v[3] << fmt->Ashift; +} + +void RageSurfaceUtils::SetRawRGBAV( uint8_t *p, const RageSurface *src, const uint8_t *v ) +{ + uint32_t pixel = SetRawRGBAV(src->format, v); + encodepixel(p, src->format->BytesPerPixel, pixel); +} + +/* Inverse of GetRGBAV. */ +uint32_t RageSurfaceUtils::SetRGBAV( const RageSurfaceFormat *fmt, const uint8_t *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 RageSurfaceUtils::SetRGBAV( uint8_t *p, const RageSurface *src, const uint8_t *v ) +{ + uint32_t pixel = SetRGBAV(src->format, v); + encodepixel(p, src->format->BytesPerPixel, pixel); +} + + +void RageSurfaceUtils::GetBitsPerChannel( const RageSurfaceFormat *fmt, uint32_t bits[4] ) +{ + /* The actual bits stored in each color is 8-loss. */ + bits[0] = 8 - fmt->Rloss; + bits[1] = 8 - fmt->Gloss; + bits[2] = 8 - fmt->Bloss; + bits[3] = 8 - fmt->Aloss; +} + +void RageSurfaceUtils::CopySurface( RageSurface *src, RageSurface *dest ) +{ + /* Copy the palette, if we have one. */ + if( src->format->BitsPerPixel == 8 && dest->format->BitsPerPixel == 8 ) + { + ASSERT( dest->format->palette ); + dest->fmt.palette = src->fmt.palette; + } + + Blit( src, dest, -1, -1, false ); +} + +bool RageSurfaceUtils::ConvertSurface( RageSurface *src, RageSurface *&dst, + int width, int height, int bpp, + uint32_t R, uint32_t G, uint32_t B, uint32_t A ) +{ + dst = CreateSurface( width, height, bpp, R, G, B, A ); + + /* If the formats are the same, no conversion is needed. */ + if( width == src->w && height == src->h && src->format == dst->format ) + { + delete dst; + dst = NULL; + return false; + } + + CopySurface( src, dst ); + return true; +} + +void RageSurfaceUtils::ConvertSurface(RageSurface *&image, + int width, int height, int bpp, + uint32_t R, uint32_t G, uint32_t B, uint32_t A) +{ + RageSurface *ret_image; + if( !ConvertSurface( image, ret_image, width, height, bpp, R, G, B, A ) ) + return; + + delete image; + image = ret_image; +} + + +/* Local helper for FixHiddenAlpha. */ +static void FindAlphaRGB(const RageSurface *img, uint8_t &r, uint8_t &g, uint8_t &b, bool reverse) +{ + r = g = b = 0; + + /* If we have no alpha, there's no alpha color. */ + if( img->format->BitsPerPixel > 8 && !img->format->Amask ) + return; + + /* Eww. Sorry. Iterate front-to-back or in reverse. */ + for(int y = reverse? img->h-1:0; + reverse? (y >=0):(y < img->h); reverse? (--y):(++y)) + { + uint8_t *row = (uint8_t *)img->pixels + img->pitch*y; + if(reverse) + row += img->format->BytesPerPixel * (img->w-1); + + for(int x = 0; x < img->w; ++x) + { + uint32_t val = RageSurfaceUtils::decodepixel(row, img->format->BytesPerPixel); + if( img->format->BitsPerPixel == 8 ) + { + if( img->format->palette->colors[val].a ) + { + /* This color isn't fully transparent, so grab it. */ + r = img->format->palette->colors[val].r; + g = img->format->palette->colors[val].g; + b = img->format->palette->colors[val].b; + return; + } + } + else + { + if( val & img->format->Amask ) + { + /* This color isn't fully transparent, so grab it. */ + img->format->GetRGB( val, &r, &g, &b ); + return; + } + } + + if( reverse ) + row -= img->format->BytesPerPixel; + else + row += img->format->BytesPerPixel; + } + } + + /* Huh? The image is completely transparent. */ + r = g = b = 0; +} + +/* Local helper for FixHiddenAlpha. Set the underlying RGB values of all pixels in + * img that are completely transparent. */ +static void SetAlphaRGB(const RageSurface *img, uint8_t r, uint8_t g, uint8_t b) +{ + /* If it's a paletted surface, all we have to do is change the palette. */ + if( img->format->BitsPerPixel == 8 ) + { + for( int c = 0; c < img->format->palette->ncolors; ++c ) + { + if( img->format->palette->colors[c].a ) + continue; + img->format->palette->colors[c].r = r; + img->format->palette->colors[c].g = g; + img->format->palette->colors[c].b = b; + } + return; + } + + + /* If it's RGBA and there's no alpha channel, we have nothing to do. */ + if( img->format->BitsPerPixel > 8 && !img->format->Amask ) + return; + + uint32_t trans; + img->format->MapRGBA( r, g, b, 0, trans ); + for( int y = 0; y < img->h; ++y ) + { + uint8_t *row = (uint8_t *)img->pixels + img->pitch*y; + + for( int x = 0; x < img->w; ++x ) + { + uint32_t val = RageSurfaceUtils::decodepixel( row, img->format->BytesPerPixel ); + if( val != trans && !(val&img->format->Amask) ) + { + RageSurfaceUtils::encodepixel( row, img->format->BytesPerPixel, trans ); + } + + row += img->format->BytesPerPixel; + } + } +} + +/* When we scale up images (which we always do in high res), pixels + * that are completely transparent can be blended with opaque pixels, + * causing their RGB elements to show. This is visible in many textures + * as a pixel-wide border in the wrong color. This is tricky to fix. + * We need to set the RGB components of completely transparent pixels + * to a reasonable color. + * + * Most images have a single border color. For these, the transparent + * color is easy: search through the image top-bottom-left-right, + * find the first non-transparent pixel, and pull out its RGB. + * + * A few images don't. We can only make a guess here. What we'll do + * is, after the above search, do the same in reverse (bottom-top-right- + * left). If the color we find is different, we'll just set the border + * color to black. + */ +void RageSurfaceUtils::FixHiddenAlpha( RageSurface *img ) +{ + uint8_t r, g, b; + FindAlphaRGB(img, r, g, b, false); + + uint8_t cr, cg, cb; /* compare */ + FindAlphaRGB(img, cr, cg, cb, true); + + if( cr != r || cg != g || cb != b ) + r = g = b = 0; + + SetAlphaRGB(img, r, g, b); +} + +/* Find various traits of a surface. Do these all at once, so we only have to + * iterate the surface once. */ +int RageSurfaceUtils::FindSurfaceTraits( const RageSurface *img ) +{ + const int NEEDS_NO_ALPHA=0, NEEDS_BOOL_ALPHA=1, NEEDS_FULL_ALPHA=2; + int alpha_type = NEEDS_NO_ALPHA; + + uint32_t max_alpha; + if( img->format->BitsPerPixel == 8 ) + max_alpha = 0xFF; + else + max_alpha = img->format->Amask; + + for(int y = 0; y < img->h; ++y) + { + uint8_t *row = (uint8_t *)img->pixels + img->pitch*y; + + for(int x = 0; x < img->w; ++x) + { + uint32_t val = decodepixel(row, img->format->BytesPerPixel); + + uint32_t alpha; + if( img->format->BitsPerPixel == 8 ) + alpha = img->format->palette->colors[val].a; + else + alpha = (val & img->format->Amask); + + if( alpha == 0 ) + alpha_type = max( alpha_type, NEEDS_BOOL_ALPHA ); + else if( alpha != max_alpha ) + alpha_type = max( alpha_type, NEEDS_FULL_ALPHA ); + + row += img->format->BytesPerPixel; + } + } + + int ret = 0; + switch( alpha_type ) + { + case NEEDS_NO_ALPHA: ret |= TRAIT_NO_TRANSPARENCY; break; + case NEEDS_BOOL_ALPHA: ret |= TRAIT_BOOL_TRANSPARENCY; break; + case NEEDS_FULL_ALPHA: break; + default: ASSERT(0); + } + + return ret; +} + + +/* Local helper for BlitTransform. */ +static inline void GetRawRGBAV_XY( const RageSurface *src, uint8_t *v, int x, int y ) +{ + const uint8_t *srcp = (const uint8_t *) src->pixels + (y * src->pitch); + const uint8_t *srcpx = srcp + (x * src->fmt.BytesPerPixel); + + RageSurfaceUtils::GetRawRGBAV( srcpx, src->fmt, v ); +} + +static inline float scale( float x, float l1, float h1, float l2, float h2 ) +{ + return ((x - l1) / (h1 - l1) * (h2 - l2) + l2); +} + +/* Completely unoptimized. */ +void RageSurfaceUtils::BlitTransform( const RageSurface *src, RageSurface *dst, + const float fCoords[8] /* TL, BR, BL, TR */ ) +{ + ASSERT( src->format->BytesPerPixel == dst->format->BytesPerPixel ); + + const float Coords[8] = { + (fCoords[0] * (src->w)), (fCoords[1] * (src->h)), + (fCoords[2] * (src->w)), (fCoords[3] * (src->h)), + (fCoords[4] * (src->w)), (fCoords[5] * (src->h)), + (fCoords[6] * (src->w)), (fCoords[7] * (src->h)) + }; + + const int TL_X = 0, TL_Y = 1, BL_X = 2, BL_Y = 3, + BR_X = 4, BR_Y = 5, TR_X = 6, TR_Y = 7; + + for( int y = 0; y < dst->h; ++y ) + { + uint8_t *dstp = (uint8_t *) dst->pixels + (y * dst->pitch); /* line */ + uint8_t *dstpx = dstp; /* pixel */ + + const float start_y = scale(float(y), 0, float(dst->h), Coords[TL_Y], Coords[BL_Y]); + const float end_y = scale(float(y), 0, float(dst->h), Coords[TR_Y], Coords[BR_Y]); + + const float start_x = scale(float(y), 0, float(dst->h), Coords[TL_X], Coords[BL_X]); + const float end_x = scale(float(y), 0, float(dst->h), Coords[TR_X], Coords[BR_X]); + + for( int x = 0; x < dst->w; ++x ) + { + const float src_xp = scale(float(x), 0, float(dst->w), start_x, end_x); + const float src_yp = scale(float(x), 0, float(dst->w), start_y, end_y); + + /* If the surface is two pixels wide, src_xp is 0..2. .5 indicates + * pixel[0]; 1 indicates 50% pixel[0], 50% pixel[1]; 1.5 indicates + * pixel[1]; 2 indicates 50% pixel[1], 50% pixel[2] (which is clamped + * to pixel[1]). */ + int src_x[2], src_y[2]; + src_x[0] = (int) truncf(src_xp - 0.5f); + src_x[1] = src_x[0] + 1; + + src_y[0] = (int) truncf(src_yp - 0.5f); + src_y[1] = src_y[0] + 1; + + /* Emulate GL_REPEAT. */ + src_x[0] = clamp(src_x[0], 0, src->w); + src_x[1] = clamp(src_x[1], 0, src->w); + src_y[0] = clamp(src_y[0], 0, src->h); + src_y[1] = clamp(src_y[1], 0, src->h); + + /* Decode our four pixels. */ + uint8_t v[4][4]; + GetRawRGBAV_XY(src, v[0], src_x[0], src_y[0]); + GetRawRGBAV_XY(src, v[1], src_x[0], src_y[1]); + GetRawRGBAV_XY(src, v[2], src_x[1], src_y[0]); + GetRawRGBAV_XY(src, v[3], src_x[1], src_y[1]); + + /* Distance from the pixel chosen: */ + float weight_x = src_xp - (src_x[0] + 0.5f); + float weight_y = src_yp - (src_y[0] + 0.5f); + + /* Filter: */ + uint8_t out[4] = { 0,0,0,0 }; + for(int i = 0; i < 4; ++i) + { + float sum = 0; + sum += v[0][i] * (1-weight_x) * (1-weight_y); + sum += v[1][i] * (1-weight_x) * (weight_y); + sum += v[2][i] * (weight_x) * (1-weight_y); + sum += v[3][i] * (weight_x) * (weight_y); + out[i] = (uint8_t) clamp( lrintf(sum), 0, 255 ); + } + + /* If the source has no alpha, set the destination to opaque. */ + if( src->format->Amask == 0 ) + out[3] = uint8_t( dst->format->Amask >> dst->format->Ashift ); + + SetRawRGBAV(dstpx, dst, out); + + dstpx += dst->format->BytesPerPixel; + } + } +} + + +/* + * Simplified: + * + * No source alpha. + * Palette -> palette blits assume the palette is identical (no mapping). + * No color key. + * No general blitting rects. + */ + +static void blit_same_type( RageSurface *src_surf, const RageSurface *dst_surf, int width, int height ) +{ + const char *src = (const char *) src_surf->pixels; + const char *dst = (const char *) dst_surf->pixels; + + /* Bytes to skip at the end of a line. */ + const int srcskip = src_surf->pitch - width*src_surf->format->BytesPerPixel; + const int dstskip = dst_surf->pitch - width*dst_surf->format->BytesPerPixel; + + /* XXX: duff's this */ + while( height-- ) + { + int x = 0; + while( x++ < width ) + { + /* (Relatively) fast. */ + switch( src_surf->format->BytesPerPixel ) + { + case 1: *((uint8_t *)dst) = *((uint8_t *)src); break; + case 2: *((uint16_t *)dst) = *((uint16_t *)src); break; + case 3: ((uint8_t *)dst)[0] = ((uint8_t *)src)[0]; + ((uint8_t *)dst)[1] = ((uint8_t *)src)[1]; + ((uint8_t *)dst)[2] = ((uint8_t *)src)[2]; + break; + case 4: *((uint32_t *)dst) = *((uint32_t *)src); break; + } + + src += src_surf->format->BytesPerPixel; + dst += dst_surf->format->BytesPerPixel; + } + + src += srcskip; + dst += dstskip; + } +} + +/* Rescaling blit with no ckey. This is used to update movies in + * D3D, so optimization is very important. */ +static void blit_rgba_to_rgba( RageSurface *src_surf, const RageSurface *dst_surf, int width, int height ) +{ + const char *src = (const char *) src_surf->pixels; + const char *dst = (const char *) dst_surf->pixels; + + /* Bytes to skip at the end of a line. */ + const int srcskip = src_surf->pitch - width*src_surf->format->BytesPerPixel; + const int dstskip = dst_surf->pitch - width*dst_surf->format->BytesPerPixel; + + uint32_t src_bits[4], dst_bits[4]; + RageSurfaceUtils::GetBitsPerChannel(src_surf->format, src_bits); + RageSurfaceUtils::GetBitsPerChannel(dst_surf->format, dst_bits); + + const int rshifts[4] = { + src_surf->format->Rshift + src_bits[0] - dst_bits[0], + src_surf->format->Gshift + src_bits[1] - dst_bits[1], + src_surf->format->Bshift + src_bits[2] - dst_bits[2], + src_surf->format->Ashift + src_bits[3] - dst_bits[3], + }; + const int lshifts[4] = { + dst_surf->format->Rshift, + dst_surf->format->Gshift, + dst_surf->format->Bshift, + dst_surf->format->Ashift, + }; + + const uint32_t masks[4] = { + src_surf->format->Rmask, + src_surf->format->Gmask, + src_surf->format->Bmask, + src_surf->format->Amask + }; + + int ormask = 0; + if( src_surf->format->Amask == 0 ) + ormask = dst_surf->format->Amask; + + while( height-- ) + { + int x = 0; + while( x++ < width ) + { + unsigned int pixel = RageSurfaceUtils::decodepixel((uint8_t *) src, src_surf->format->BytesPerPixel); + + /* Convert pixel to the destination RGBA. */ + unsigned int opixel = 0; + opixel |= (pixel & masks[0]) >> rshifts[0] << lshifts[0]; + opixel |= (pixel & masks[1]) >> rshifts[1] << lshifts[1]; + opixel |= (pixel & masks[2]) >> rshifts[2] << lshifts[2]; + opixel |= (pixel & masks[3]) >> rshifts[3] << lshifts[3]; + + // Correct surfaces that don't have an alpha channel. + opixel |= ormask; + + /* Store it. */ + RageSurfaceUtils::encodepixel((uint8_t *) dst, dst_surf->format->BytesPerPixel, opixel); + + src += src_surf->format->BytesPerPixel; + dst += dst_surf->format->BytesPerPixel; + } + + src += srcskip; + dst += dstskip; + } +} + +static void blit_generic( RageSurface *src_surf, const RageSurface *dst_surf, int width, int height ) +{ + const char *src = (const char *) src_surf->pixels; + const char *dst = (const char *) dst_surf->pixels; + + /* Bytes to skip at the end of a line. */ + const int srcskip = src_surf->pitch - width*src_surf->format->BytesPerPixel; + const int dstskip = dst_surf->pitch - width*dst_surf->format->BytesPerPixel; + + while( height-- ) + { + int x = 0; + while( x++ < width ) + { + unsigned int pixel = RageSurfaceUtils::decodepixel((uint8_t *) src, src_surf->format->BytesPerPixel); + + uint8_t colors[4]; + /* Convert pixel to the destination RGBA. */ + colors[0] = src_surf->format->palette->colors[pixel].r; + colors[1] = src_surf->format->palette->colors[pixel].g; + colors[2] = src_surf->format->palette->colors[pixel].b; + colors[3] = src_surf->format->palette->colors[pixel].a; + pixel = RageSurfaceUtils::SetRGBAV(dst_surf->format, colors); + + /* Store it. */ + RageSurfaceUtils::encodepixel((uint8_t *) dst, dst_surf->format->BytesPerPixel, pixel); + + src += src_surf->format->BytesPerPixel; + dst += dst_surf->format->BytesPerPixel; + } + + src += srcskip; + dst += dstskip; + } + +} + +/* Blit src onto dst. */ +void RageSurfaceUtils::Blit( RageSurface *src, RageSurface *dst, int width, int height, bool ckey ) +{ + if(width == -1) + width = src->w; + if(height == -1) + height = src->h; + width = min(src->w, dst->w); + height = min(src->h, dst->h); + + /* Types of blits: + * RGBA->RGBA, same format without colorkey + * RGBA->RGBA, same format with colorkey + * PAL->PAL; ignore colorkey flag + * RGBA->RGBA different format without colorkey + * RGBA->RGBA different format with colorkey + * PAL->RGBA with colorkey + * PAL->RGBA without colorkey + */ + if( src->format->BytesPerPixel == dst->format->BytesPerPixel && + src->format->Rmask == dst->format->Rmask && + src->format->Gmask == dst->format->Gmask && + src->format->Bmask == dst->format->Bmask && + src->format->Amask == dst->format->Amask ) + { + /* RGBA->RGBA with the same format, or PAL->PAL. Simple copy. */ + blit_same_type(src, dst, width, height); + } + + else if( src->format->BytesPerPixel != 1 && dst->format->BytesPerPixel != 1 ) + { + /* RGBA->RGBA with different formats. */ + blit_rgba_to_rgba(src, dst, width, height); + } + + else if( src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel != 1 ) + { + /* PAL->RGBA. */ + blit_generic(src, dst, width, height); + } + else + /* We don't do RGBA->PAL. */ + ASSERT(0); + + /* + * The destination surface may be larger than the source. For example, we may be + * blitting a 200x200 image onto a 256x256 surface for OpenGL. Normally, that extra + * space isn't actually used; we'll only render the image space. However, bilinear + * filtering will cause the lines of pixels at 201x... and ...x201 to be visible. We + * need to make sure those pixels make sense. + * + * Previously, we just cleared the image to transparent or the color key. This + * has two problems. First, we may not have space for a color key (an image with + * 256 non-transparent palette colors). Second, that's not completely correct; + * it'll force the outside border of the image to filter to transparent. If the image + * is being tiled with another image, that may leave seams. + * + * (In some cases, filtering to transparent is preferable, particularly when displaying + * a sprite in perspective. If you want that, add blank space to the image explicitly.) + * + * Copy the last column (200x... -> 201x...), then the last row (...x200 -> ...x201). + */ + + if( width < dst->w ) + { + /* Duplicate the last column. */ + int offset = dst->format->BytesPerPixel * (width-1); + uint8_t *p = (uint8_t *) dst->pixels + offset; + + for( int y = 0; y < height; ++y ) + { + uint32_t pixel = decodepixel( p, dst->format->BytesPerPixel ); + encodepixel( p+dst->format->BytesPerPixel, dst->format->BytesPerPixel, pixel ); + + p += dst->pitch; + } + } + + if( height < dst->h ) + { + /* Duplicate the last row. */ + char *srcp = (char *) dst->pixels; + srcp += dst->pitch * (height-1); + memcpy( srcp + dst->pitch, srcp, dst->pitch ); + } +} + +struct SurfaceHeader +{ + int width, height, pitch; + int Rmask, Gmask, Bmask, Amask; + int bpp; +}; + +/* Save and load RageSurfaces to disk, in a very fast, nonportable way. */ +bool RageSurfaceUtils::SaveSurface( RageSurface *img, CString file ) +{ + RageFile f; + if( !f.Open( file, RageFile::WRITE ) ) + return false; + + SurfaceHeader h; + memset( &h, 0, sizeof(h) ); + + h.height = img->h; + h.width = img->w; + h.pitch = img->pitch; + h.Rmask = img->format->Rmask; + h.Gmask = img->format->Gmask; + h.Bmask = img->format->Bmask; + h.Amask = img->format->Amask; + h.bpp = img->format->BitsPerPixel; + + f.Write( &h, sizeof(h) ); + + if(h.bpp == 8) + f.Write( img->format->palette->colors, 256 * sizeof(RageSurfaceColor) ); + + f.Write( img->pixels, img->h * img->pitch ); + + return true; +} + +RageSurface *RageSurfaceUtils::LoadSurface( CString file ) +{ + RageFile f; + if( !f.Open( file ) ) + return false; + + SurfaceHeader h; + if( f.Read( &h, sizeof(h) ) != sizeof(h) ) + return NULL; + + RageSurfaceColor palette[256]; + if(h.bpp == 8) + if( f.Read( palette, 256 * sizeof(RageSurfaceColor) != 256 * sizeof(RageSurfaceColor) ) ) + return NULL; + + /* Create the surface. */ + RageSurface *img = CreateSurface( h.width, h.height, h.bpp, + h.Rmask, h.Gmask, h.Bmask, h.Amask ); + ASSERT( img ); + ASSERT( h.pitch == img->pitch ); + + if( f.Read( img->pixels, h.height * h.pitch ) != h.height * h.pitch ) + { + delete img; + return NULL; + } + + /* Set the palette. */ + if( h.bpp == 8 ) + memcpy( img->fmt.palette->colors, palette, 256*sizeof(RageSurfaceColor) ); + + return img; +} + +/* + * This converts an image to a special 8-bit paletted format. The palette is set up + * so that palette indexes look like regular, packed components. + * + * For example, an image with 8 bits of grayscale and 0 bits of alpha has a palette + * that looks like { 0,0,0,255 }, { 1,1,1,255 }, { 2,2,2,255 }, ... { 255,255,255,255 }. + * This results in index components that can be treated as grayscale values. + * + * An image with 2 bits of grayscale and 2 bits of alpha look like + * { 0,0,0,0 }, { 85,85,85,0 }, { 170,170,170,0 }, { 255,255,255,0 }, + * { 0,0,0,85 }, { 85,85,85,85 }, { 170,170,170,85 }, { 255,255,255,85 }, ... + * + * This results in index components that can be pulled apart like regular packed + * values: the first two bits of the index are the grayscale component, and the next + * two bits are the alpha component. + * + * This gives us a generic way to handle arbitrary 8-bit texture formats. + */ +RageSurface *RageSurfaceUtils::Palettize( RageSurface *src_surf, int GrayBits, int AlphaBits ) +{ + AlphaBits = min( AlphaBits, 8-src_surf->format->Aloss ); + + const int TotalBits = GrayBits + AlphaBits; + ASSERT( TotalBits <= 8 ); + + RageSurface *dst_surf = CreateSurface(src_surf->w, src_surf->h, + 8, 0,0,0,0 ); + + /* Set up the palette. */ + const int TotalColors = 1 << TotalBits; + const int Ivalues = 1 << GrayBits; // number of intensity values + const int Ishift = 0; // intensity shift + const int Imask = ((1 << GrayBits) - 1) << Ishift; // intensity mask + const int Iloss = 8-GrayBits; + + const int Avalues = 1 << AlphaBits; // number of alpha values + const int Ashift = GrayBits; // alpha shift + const int Amask = ((1 << AlphaBits) - 1) << Ashift; // alpha mask + const int Aloss = 8-AlphaBits; + + for( int index = 0; index < TotalColors; ++index ) + { + const int I = (index & Imask) >> Ishift; + const int A = (index & Amask) >> Ashift; + + int ScaledI; + if( Ivalues == 1 ) + ScaledI = 255; // if only one intensity value, always fullbright + else + ScaledI = clamp( int(roundf(I * (255.0f / (Ivalues-1)))), 0, 255 ); + + int ScaledA; + if( Avalues == 1 ) + ScaledA = 255; // if only one alpha value, always opaque + else + ScaledA = clamp( int(roundf(A * (255.0f / (Avalues-1)))), 0, 255 ); + + RageSurfaceColor c; + c.r = uint8_t(ScaledI); + c.g = uint8_t(ScaledI); + c.b = uint8_t(ScaledI); + c.a = uint8_t(ScaledA); + + dst_surf->fmt.palette->colors[index] = c; + } + + const char *src = (const char *) src_surf->pixels; + const char *dst = (const char *) dst_surf->pixels; + + int height = src_surf->h; + int width = src_surf->w; + + /* Bytes to skip at the end of a line. */ + const int srcskip = src_surf->pitch - width*src_surf->format->BytesPerPixel; + const int dstskip = dst_surf->pitch - width*dst_surf->format->BytesPerPixel; + + while( height-- ) + { + int x = 0; + while( x++ < width ) + { + unsigned int pixel = decodepixel((uint8_t *) src, src_surf->format->BytesPerPixel); + + uint8_t colors[4]; + GetRGBAV(pixel, src_surf, colors); + + int Ival = 0; + Ival += colors[0]; + Ival += colors[1]; + Ival += colors[2]; + Ival /= 3; + + pixel = (Ival >> Iloss) << Ishift | + (colors[3] >> Aloss) << Ashift; + + /* Store it. */ + *((uint8_t *) dst) = uint8_t(pixel); + + src += src_surf->format->BytesPerPixel; + dst += dst_surf->format->BytesPerPixel; + } + + src += srcskip; + dst += dstskip; + } + + return dst_surf; +} + + +RageSurface *RageSurfaceUtils::MakeDummySurface( int height, int width ) +{ + RageSurface *ret_image = CreateSurface( width, height, 8, 0,0,0,0 ); + + RageSurfaceColor pink( 0xFF, 0x10, 0xFF, 0xFF ); + ret_image->fmt.palette->colors[0] = pink; + + memset( ret_image->pixels, 0, ret_image->h*ret_image->pitch ); + + return ret_image; +} + +/* HACK: Some banners and textures have #F800F8 as the color key. Search the edge + * it; if we find it, use that as the color key. */ +static bool ImageUsesOffHotPink( const RageSurface *img ) +{ + uint32_t OffHotPink; + if( !img->format->MapRGBA( 0xF8, 0, 0xF8, 0xFF, OffHotPink ) ) + return false; + + const uint8_t *p = img->pixels; + for( int x = 0; x < img->w; ++x ) + { + uint32_t val = RageSurfaceUtils::decodepixel( p, img->format->BytesPerPixel ); + if( val == OffHotPink ) + return true; + p += img->format->BytesPerPixel; + } + + p = img->pixels; + p += img->pitch * (img->h-1); + for( int i=0; i < img->w; i++ ) + { + uint32_t val = RageSurfaceUtils::decodepixel( p, img->format->BytesPerPixel ); + if( val == OffHotPink ) + return true; + p += img->format->BytesPerPixel; + } + return false; +} + +/* Set #FF00FF and #F800F8 to transparent. img may be reallocated if it has no alpha + * bits. */ +void RageSurfaceUtils::ApplyHotPinkColorKey( RageSurface *&img ) +{ + if( img->format->BitsPerPixel == 8 ) + { + uint32_t color; + if( img->format->MapRGBA( 0xF8, 0, 0xF8, 0xFF, color ) ) + img->format->palette->colors[ color ].a = 0; + if( img->format->MapRGBA( 0xFF, 0, 0xFF, 0xFF, color ) ) + img->format->palette->colors[ color ].a = 0; + return; + } + + /* RGBA. */ + /* Make sure we have alpha. */ + if( !img->format->Amask ) + { + /* We don't have any alpha. Try to enable it without copying. */ + int usable_bits = (1<format->BitsPerPixel)-1; + usable_bits &= ~img->format->Rmask; + usable_bits &= ~img->format->Gmask; + usable_bits &= ~img->format->Bmask; + + for( int i = 0; img->format->Amask == 0 && i < 32; ++i ) + { + if( usable_bits & (1<format->Amask = 1<format->Aloss = 7; + img->format->Ashift = (uint8_t) i; + } + } + + if( img->format->Amask == 0 ) + ConvertSurface( img, img->w, img->h, + 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF ); + } + + uint32_t HotPink; + + bool bHaveColorKey; + if( ImageUsesOffHotPink(img) ) + bHaveColorKey = img->format->MapRGBA( 0xF8, 0, 0xF8, 0xFF, HotPink ); + else + bHaveColorKey = img->format->MapRGBA( 0xFF, 0, 0xFF, 0xFF, HotPink ); + if( !bHaveColorKey ) + return; + + for( int y = 0; y < img->h; ++y ) + { + uint8_t *row = (uint8_t *)img->pixels + img->pitch*y; + + for( int x = 0; x < img->w; ++x ) + { + uint32_t val = decodepixel( row, img->format->BytesPerPixel ); + if( val == HotPink ) + encodepixel( row, img->format->BytesPerPixel, 0 ); + + row += img->format->BytesPerPixel; + } + } +} + +/* + * (c) 2001-2004 Glenn Maynard, Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/RageSurfaceUtils.h b/stepmania/src/RageSurfaceUtils.h new file mode 100644 index 0000000000..58b3cfe8d0 --- /dev/null +++ b/stepmania/src/RageSurfaceUtils.h @@ -0,0 +1,85 @@ +#ifndef RAGE_SURFACE_UTILS_H +#define RAGE_SURFACE_UTILS_H + +struct RageSurfaceColor; +struct RageSurfacePalette; +struct RageSurfaceFormat; +struct RageSurface; + +namespace RageSurfaceUtils +{ + uint32_t decodepixel( const uint8_t *p, int bpp ); + void encodepixel( uint8_t *p, int bpp, uint32_t pixel ); + + void GetRawRGBAV( uint32_t pixel, const RageSurfaceFormat &fmt, uint8_t *v ); + void GetRawRGBAV( const uint8_t *p, const RageSurfaceFormat &fmt, uint8_t *v ); + void GetRGBAV( uint32_t pixel, const RageSurface *src, uint8_t *v ); + void GetRGBAV( const uint8_t *p, const RageSurface *src, uint8_t *v ); + + uint32_t SetRawRGBAV( const RageSurfaceFormat *fmt, const uint8_t *v ); + void SetRawRGBAV( uint8_t *p, const RageSurface *src, const uint8_t *v ); + uint32_t SetRGBAV( const RageSurfaceFormat *fmt, const uint8_t *v ); + void SetRGBAV( uint8_t *p, const RageSurface *src, const uint8_t *v ); + + /* Get the number of bits representing each color channel in fmt. */ + void GetBitsPerChannel( const RageSurfaceFormat *fmt, uint32_t bits[4] ); + + void CopySurface( RageSurface *src, RageSurface *dest ); + bool ConvertSurface( RageSurface *src, RageSurface *&dst, + int width, int height, int bpp, uint32_t R, uint32_t G, uint32_t B, uint32_t A ); + void ConvertSurface( RageSurface *&image, + int width, int height, int bpp, uint32_t R, uint32_t G, uint32_t B, uint32_t A ); + + void FixHiddenAlpha( RageSurface *img ); + + int FindSurfaceTraits( const RageSurface *img ); + + /* The surface contains no transparent pixels and/or never uses its color + * key, so it doesn't need any alpha bits at all. */ + enum { TRAIT_NO_TRANSPARENCY = 0x0001 }; /* 0alpha */ + + /* The surface contains only transparent values of 0 or 1; no translucency. + * It only needs one bit of alpha. */ + enum { TRAIT_BOOL_TRANSPARENCY = 0x0002 }; /* 1alpha */ + + void BlitTransform( const RageSurface *src, RageSurface *dst, + const float fCoords[8] /* TL, BR, BL, TR */ ); + + void Blit( RageSurface *src, RageSurface *dst, int width, int height, bool ckey ); + + bool SaveSurface( RageSurface *img, CString file ); + RageSurface *LoadSurface( CString file ); + + RageSurface *Palettize( RageSurface *src_surf, int GrayBits, int AlphaBits ); + + RageSurface *MakeDummySurface( int height, int width ); + + void ApplyHotPinkColorKey( RageSurface *&img ); +}; + +#endif + +/* + * (c) 2001-2004 Glenn Maynard, Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */