RageSurface, to replace SDL_Surface.
Most of SDL_utils is moved to RageSurfaceUtils, and does not require SDL; SDL_utils is now only utils that actually need SDL. (My goal isn't to eliminate SDL; it's to make it a back-end library, that can be disabled on some platforms, rather than a required library as it is now.)
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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<<img->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<<i) )
|
||||
{
|
||||
img->format->Amask = 1<<i;
|
||||
img->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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
Reference in New Issue
Block a user