/* $Id: sprite.cpp,v 1.17.4.1 2006/01/20 11:23:31 chfreund Exp $ */ #include #include "sprite.hpp" #include "global.hpp" #include "graphics.hpp" #define USE_LOADERx #ifdef USE_LOADER #include "loader.hpp" #else #include #endif using namespace std; /**********************************************************/ #ifdef SUPPRESS_COLORED_OUTPUT #define RED(s) s #define BLACK_(s) s #define CYAN(s) s #else #define RED(s) "\033[31m" << s << "\033[0m" #define BLACK_(s) "\033[0;1m" << s << "\033[0m" #define CYAN(s) "\033[36m" << s << "\033[0m" #endif // SUPPRESS_COLORING #ifndef FUNCTION_ERROR #define FUNCTION_ERROR(fns) RED(fns) << "\n >> " #endif // FUNCTION_ERROR #ifndef CALLED_FROM #define CALLED_FROM(fns) " called from " << CYAN(fns) << "\n" #endif // CALLED_FROM /**********************************************************/ Sprite::Sprite() : m_Surface( NULL ), m_HotSpotX( 0 ), m_HotSpotY( 0 ), m_Flags( UNSPECIFIED ) { } /**********************************************************/ Sprite::Sprite( const Sprite& sprite ) : m_Surface( NULL ), m_HotSpotX( 0 ), m_HotSpotY( 0 ), m_Flags( UNSPECIFIED ) { *this = sprite; } /**********************************************************/ Sprite::~Sprite() { LOG( 5 ) INFO( "Sprite::~Sprite: starting destructor\n" ); reset(); LOG( 5 ) INFO( "Sprite::~Sprite: done\n" ); } /**********************************************************/ const Sprite& Sprite::operator = ( const Sprite& sprite ) { const char fn[] = "Sprite:: operator = "; // prevent the standard source of bugs ;-> if( &sprite == this ) return sprite; // reset the sprite first reset(); // create a new surface m_Surface = SDL_CreateRGBSurface( sprite.getSurface()->flags, sprite.getSurface()->w, sprite.getSurface()->h, sprite.getSurface()->format->BitsPerPixel, sprite.getSurface()->format->Rmask, sprite.getSurface()->format->Gmask, sprite.getSurface()->format->Bmask, sprite.getSurface()->format->Amask ); // check the success of the surface creation if( m_Surface != NULL ) { // copy the flat data m_HotSpotX = sprite.getHotSpotX(); m_HotSpotY = sprite.getHotSpotY(); // transfer the image memcpy( m_Surface->pixels, sprite.getSurface()->pixels, sprite.getSurface()->h * sprite.getSurface()->pitch ); // set color key if( 0 != (sprite.getFlags() & COLORKEY) ) { #ifdef _DEBUG_ if( !setColorKey(sprite.getSurface()->format->colorkey) ) { cerr << CALLED_FROM( fn ); } #endif setColorKey( sprite.getSurface()->format->colorkey ); } // set alpha value if( 0 != (sprite.getFlags() & CONST_ALPHA) ) { #ifdef _DEBUG_ if( !setAlphaValue( sprite.getSurface()->format->alpha) ) { cerr << CALLED_FROM( fn ); } #endif setAlphaValue( sprite.getSurface()->format->alpha ); } } else { cerr << FUNCTION_ERROR( fn ) << "could not create a new SDL_Surface\n"; } return *this; } /**********************************************************/ bool Sprite::getRGBA( const int x, const int y, Uint8& red, Uint8& green, Uint8& blue, Uint8& alpha ) const { #ifdef _DEBUG_ const char fn[] = "Sprite::getRGBA"; // check validity of the image if( NULL == m_Surface ) { cerr << FUNCTION_ERROR( fn ) << "sprite does not contain a valid SDL surface\n"; return false; } // if( x < 0 || x >= m_Surface->w || y < 0 || y >= m_Surface->h ) { cerr << FUNCTION_ERROR( fn ) << "("<w<<","<< m_Surface->h << ")\n"; return false; } #endif alpha = 0; const Uint32 pixel = Graphics::getPixel( m_Surface, x, y ); // get pixel value Uint8 tempalpha = 0; SDL_GetRGBA( pixel, m_Surface->format, &red, &green, &blue, &tempalpha ); // only if this sprite has colorkey and the pixel is visible, // we must change the alpha value, namely to opaq if( (m_Surface->flags & SDL_SRCCOLORKEY) != 0 ) { if( SDL_MapRGB(m_Surface->format, red, green, blue) != m_Surface->format->colorkey ) { alpha = 255; } } else { alpha = tempalpha; } return true; } /********************************************************** * Color key setting in all variants. **********************************************************/ bool Sprite::setColorKey( const Uint32 key, const bool useRLEKey ) { const Uint32 flag = SDL_SRCCOLORKEY | (useRLEKey ? SDL_RLEACCEL : 0); #ifdef _DEBUG_ const char fn[] = "SetColorKey(Uint32,bool)"; // check validity of the image if( NULL == m_Surface ) { cerr << FUNCTION_ERROR( fn ) << "cannot set color key of a NULL image\n"; return false; } // catch error of the SDL function if( SDL_SetColorKey(m_Surface, flag, key) ) { cerr << FUNCTION_ERROR("Sprite::SetColorKey") << "failed SDL_SetColorKey(" << m_Surface << (SDL_SRCCOLORKEY | flag) << key << endl; return false; } if( key ) m_Flags |= COLORKEY; else m_Flags &= ~COLORKEY; return true; #else if( !SDL_SetColorKey(m_Surface, SDL_SRCCOLORKEY | flag, key) ) { if( key ) m_Flags |= COLORKEY; else m_Flags &= ~COLORKEY; return true; } else { return false; } #endif // _DEBUG_ } /**********************************************************/ bool Sprite::setColorKey( const Uint8 red, const Uint8 green, const Uint8 blue, const bool useRLEKey ) { const Uint32 flag = SDL_SRCCOLORKEY | (useRLEKey ? SDL_RLEACCEL : 0); #ifdef _DEBUG_ const char fn[] = "SetColorKey(Uint8,Uint8,Uint8,bool)"; // check validity of the imageSprite:: if( NULL == m_Surface ) { cerr << FUNCTION_ERROR( fn ) << "cannot create key color using a NULL image\n"; return false; } // call own member function if( false == setColorKey(SDL_MapRGB(m_Surface->format, red, green, blue), flag) ) { cerr << CALLED_FROM( fn ); return false; } return true; #else // use class own member function return setColorKey( SDL_MapRGB(m_Surface->format, red, green, blue), flag ); #endif // _DEBUG_ } /**********************************************************/ bool Sprite::removeColorKey() { #ifdef _DEBUG_ if( false == setColorKey(0, 0) ) { cerr << CALLED_FROM("Sprite::removeColorKey"); return false; } return true; #else return setColorKey( 0, 0 ); #endif // _DEBUG_ } /**********************************************************/ bool Sprite::setAlphaValue( const Uint8 alpha ) { if( m_Surface ) { #ifdef _DEBUG_ if( SDL_SetAlpha(m_Surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha) ) { cerr << FUNCTION_ERROR("Sprite::setAlphaValue") << "failed call of SDL_SetAlpha\n"; return false; } else { return true; } #else if( !SDL_SetAlpha(m_Surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha) ) { m_Flags |= CONST_ALPHA; return true; } else { return false; } #endif // _DEBUG_ } DBG(1) cerr << FUNCTION_ERROR("Sprite::setAlphaValue") << "no image surface present " << m_Surface << "\n"; return false; } /**********************************************************/ Uint8 Sprite::getPixelAlpha( const int x, const int y ) const { Uint8 alpha = 0; const char fn[] = "Sprite::getPixelAlpha"; #ifdef _DEBUG_ if( m_Surface == NULL ) { cerr << FUNCTION_ERROR(fn) << "no SDL surface present\n"; return alpha; } if( x < 0 || getWidth() >= x || y < 0 || getHeight() >= y ) { cerr << FUNCTION_ERROR(fn) << "pixel ("<format, &red, &green, &blue, &alpha ); // only if this sprite has colorkey and the pixel is visible, // we must change the alpha value, namely to opaq if( ((m_Surface->flags & SDL_SRCCOLORKEY) != 0 && SDL_MapRGB(m_Surface->format, red, green, blue) != m_Surface->format->colorkey) ) { alpha = 255; } return alpha; } /********************************************************** * reset the object **********************************************************/ void Sprite::reset() { if( m_Surface ) { SDL_FreeSurface( m_Surface ); m_Surface = NULL; } m_HotSpotX = 0; m_HotSpotY = 0; m_Flags = UNSPECIFIED; } /**********************************************************/ bool Sprite::loadImage( const char* const filename ) { // load new image #ifdef USE_LOADER SDL_Surface* newImage = Loader::getInstance()->getImage( filename, true ); #else SDL_Surface* newImage = IMG_Load( filename ); #endif if( newImage ) { // remove present image and set new image if( m_Surface ) SDL_FreeSurface( m_Surface ); m_Surface = newImage; } else { cerr << FUNCTION_ERROR("Sprite::loadImage") << "cannot load image from " << BLACK_("\"" << filename << "\"") << endl; return false; } return true; } /**********************************************************/ bool Sprite::optimize( const bool alpha ) { if( m_Surface ) { // optimize image for the display format SDL_Surface *newImage = alpha ? SDL_DisplayFormatAlpha( m_Surface ) : SDL_DisplayFormat( m_Surface ); // check, if that worked if( newImage ) { SDL_FreeSurface( m_Surface ); m_Surface = newImage; return true; } else { DBG(1) cerr << FUNCTION_ERROR("Sprite::optimize") << "failed call of SDL_DisplayFormat(Alpha)\n"; return false; } // if no image is present, just leave } else { DBG(1) cerr << FUNCTION_ERROR("Sprite::optimize") << "no image surface present\n"; return true; } } /**********************************************************/ #define BRIGHTNESS_RED 30 #define BRIGHTNESS_GREEN 59 #define BRIGHTNESS_BLUE 11 #define FULL_BRIGHTNESS (BRIGHTNESS_RED + BRIGHTNESS_GREEN + BRIGHTNESS_BLUE) bool Sprite::colorGrayPixels( const Uint8 red, const Uint8 green, const Uint8 blue, const int tolerance ) { if( !m_Surface ) return false; SDL_PixelFormat* PixelFormat = m_Surface->format; for( int y = 0; y < m_Surface->h; y++ ) { for ( int x = 0; x < m_Surface->w; x++ ) { Uint32 pixel = Graphics::getPixel( m_Surface, x, y ); // temporary color variables Uint8 iRed, iGreen, iBlue, iAlpha; Uint32 ColorWithoutAlpha; // get RGBA values SDL_GetRGBA( pixel, PixelFormat, &iRed, &iGreen, &iBlue, &iAlpha ); ColorWithoutAlpha = SDL_MapRGB( PixelFormat, iRed, iGreen, iBlue ); // the whole circus only, if the pixel is visible, i.e. // alpha chanal > 0 if(( ((m_Surface->flags & SDL_SRCALPHA) && iAlpha > 0) // OR this is a color key pixel ||(( m_Surface->flags & SDL_SRCCOLORKEY &&(ColorWithoutAlpha != PixelFormat->colorkey)))) // AND if we have a grayscale pixel && ( abs(iRed - iGreen) <= tolerance && abs(iGreen - iBlue ) <= tolerance && abs(iBlue - iRed ) <= tolerance) ) { // calculate brightness of the gray pixel real brightness = BRIGHTNESS_RED * iRed + BRIGHTNESS_GREEN * iGreen + BRIGHTNESS_BLUE * iBlue; brightness /= (FULL_BRIGHTNESS * 0xff); // compute the new pixel const real realRed = red * brightness, realGreen = green * brightness, realBlue = blue * brightness; Uint8 Red = (Uint8)(realRed > 0xff ? 0xff : realRed), Green = (Uint8)(realGreen > 0xff ? 0xff : realGreen), Blue = (Uint8)(realBlue > 0xff ? 0xff : realBlue); // permit coloring with the color key if( (m_Flags & COLORKEY) && ( SDL_MapRGB(PixelFormat, Red, Green, Blue) == PixelFormat->colorkey) ) { if( !Red && !Green && !Blue ) Blue++; else if( Blue > 0 ) Blue--; else if( Red > 0 ) Red--; else Green--; } // the new pixel also contains the alpha chanal const Uint32 newPixel = SDL_MapRGBA( PixelFormat, Red, Green, Blue, iAlpha ); // write the new pixel Graphics::setPixel( m_Surface, x, y, newPixel ); } } } return true; } /**********************************************************/ bool Sprite::smooth( const real centerweight ) { const real divisionfactor[6] = { 0.0, 1.0/( centerweight), 1.0/(1+centerweight), 1.0/(2+centerweight), 1.0/(3+centerweight), 1.0/(4+centerweight) }; SDL_PixelFormat* PixelFormat = m_Surface->format; for( int y = 0; y < m_Surface->h; y++ ) { for ( int x = 0; x < m_Surface->w; x++ ) { Uint32 pixel = Graphics::getPixel( m_Surface, x, y ); Uint8 iRed, iGreen, iBlue, iAlpha; SDL_GetRGBA( pixel, PixelFormat, &iRed, &iGreen, &iBlue, &iAlpha ); // only smooth this pixel, if it is visible if( ((m_Surface->flags & SDL_SRCALPHA ) != 0 && iAlpha > 0) || ((m_Surface->flags & SDL_SRCCOLORKEY) != 0 && SDL_MapRGB(PixelFormat, iRed, iGreen, iBlue) != PixelFormat->colorkey) ) { int nPixels = 1; real fRed = centerweight * iRed, fGreen = centerweight * iGreen, fBlue = centerweight * iBlue; // pixels in the neigbourhood Uint32 neighbours[4]; int numberNeighbours = 0; if ( y > 0 ) { neighbours[numberNeighbours] = Graphics::getPixel( m_Surface, x, y-1 ); numberNeighbours++; } if ( y < m_Surface->h-1 ) { neighbours[numberNeighbours] = Graphics::getPixel( m_Surface, x, y+1 ); numberNeighbours++; } if ( x > 0 ) { neighbours[numberNeighbours] = Graphics::getPixel( m_Surface, x-1, y ); numberNeighbours++; } if ( x < m_Surface->w-1 ) { neighbours[numberNeighbours] = Graphics::getPixel( m_Surface, x+1, y ); numberNeighbours++; } for( int n = 0; n < numberNeighbours; n++ ) { SDL_GetRGBA( neighbours[n], PixelFormat, &iRed, &iGreen, &iBlue, &iAlpha ); // the neighbour pixel only contributes, if it is visible if( ((m_Surface->flags & SDL_SRCALPHA ) != 0 && iAlpha > 0) || ((m_Surface->flags & SDL_SRCCOLORKEY) != 0 && SDL_MapRGB(PixelFormat, iRed, iGreen, iBlue) != PixelFormat->colorkey) ) { fRed += iRed; fGreen += iGreen; fBlue += iBlue; nPixels++; } } // calculate and set new pixel value if( nPixels > 1 ) { iRed = (Uint8)(fRed * divisionfactor[nPixels]); iGreen = (Uint8)(fGreen * divisionfactor[nPixels]); iBlue = (Uint8)(fBlue * divisionfactor[nPixels]); // the new pixel also contains the alpha chanal const Uint32 newPixel = SDL_MapRGBA( PixelFormat, iRed, iGreen, iBlue, iAlpha ); // write the new pixel Graphics::setPixel( m_Surface, x, y, newPixel ); } } } } return true; } /**********************************************************/ void Sprite::draw( SDL_Surface* background, const int x, const int y, SDL_Rect* srcRect ) const { DBG(2) if( NULL == m_Surface ) { cerr << FUNCTION_ERROR("Sprite::draw") << "attempt to draw a NULL image\n"; return; } // SDL_BlitSurface does not care about height and width // of the passed destination rectangle, only x and y are used. SDL_Rect destRect = { x - m_HotSpotX, y - m_HotSpotY, 0, 0 }; // Note that SDL draws the whole image, if srcRect == NULL SDL_BlitSurface( m_Surface, srcRect, background, &destRect ); } /**********************************************************/ bool Sprite::draw( Sprite &target, const int x, const int y, SDL_Rect* srcRect, const bool useHotSpot, const int sourceSpecifier, const int targetSpecifier ) const { const char fn[] = "Sprite::draw"; DBG(2) { if( NULL == m_Surface ) { cerr << FUNCTION_ERROR(fn) << "attempt to draw a NULL image\n"; return false; } if( this == &target ) { cerr << FUNCTION_ERROR(fn) << "drawing sprite on itself " "will cause a crash in non-debug mode\n"; return true; } } // SDL_BlitSurface does not care about height and width // of the passed destination rectangle, only x and y are used. SDL_Rect destRect = { x, y, 0, 0 }; if( useHotSpot ) { destRect.x += m_HotSpotX; destRect.y += m_HotSpotY; } if( (sourceSpecifier & VAR_ALPHA) || (targetSpecifier & VAR_ALPHA) ) { cerr << "\033[31mSprite::draw on another sprite not completely " "implemented\033[0m\n"; } else { // Note that SDL draws the whole image, if srcRect == NULL SDL_BlitSurface( m_Surface, srcRect, target.getSurface(), &destRect ); } return true; } /**********************************************************/