#include "Surface.h" // empty constructor graphic::Surface::Surface() { _opacity = SDL_ALPHA_OPAQUE; #ifdef WITH_OPENGL _need_tex_update = false; #endif _rectangle = new SDL_Rect; _trans = false; }; // create a surface starting from a SDL_Surface graphic::Surface::Surface(SDL_Surface * surface, bool graphic_mode) { // set opacity to opaque (full visible) _opacity = SDL_ALPHA_OPAQUE; // store the SDL_Surface _surface = surface; _rectangle = new SDL_Rect; // Setting default position _rectangle->x = 0; _rectangle->y = 0; // Setting surface dimensions _rectangle->w = _surface->w; _rectangle->h = _surface->h; // SDL SURFACE if(graphic_mode == SDL_GRAPHIC) { SDL_SetAlpha(_surface, SDL_SRCALPHA|SDL_RLEACCEL , _opacity); #ifdef WITH_OPENGL _wreal = _rectangle->w; _hreal = _rectangle->h; _tex_num = 0; #endif } #ifdef WITH_OPENGL // GL SURFACE else { SDL_SetAlpha(_surface, SDL_RLEACCEL , _opacity); // Checking for max texture size if (!CheckTexSize(_surface)) throw Exception("Surface", "Max texture size exceeded\n"); // Setting real texture dimensions to be power-of-two _wreal = (int)powf(2.0, ceilf(logf((float)_surface->w)/logf(2.0f))); _hreal = (int)powf(2.0, ceilf(logf((float)_surface->h)/logf(2.0f))); if (!Surf2Tex(_surface)) throw Exception("Surface", "Texture not created, image format unknown\n"); } _need_tex_update = false; #endif _graphic_mode = graphic_mode; } // a new surface graphic::Surface::Surface(Uint16 w, Uint16 h, bool graphic_mode, Uint32 flags, bool trans, Uint16 bpp) { Uint32 rmask, gmask, bmask, amask; // transparent surface if(trans) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif } // opaque surface else { rmask = 0; gmask = 0; bmask = 0; amask = 0; } _trans = false; // Set opacity to opaque (full visible) _opacity = SDL_ALPHA_OPAQUE; _rectangle = new SDL_Rect; // Setting default position _rectangle->x = 0; _rectangle->y = 0; // Setting surface dimensions _rectangle->w = w; _rectangle->h = h; // create surface _surface = SDL_CreateRGBSurface(flags, w, h, bpp, rmask, gmask, bmask, amask); // an error is occurred if(_surface == NULL) throw Exception("Surface", SDL_GetError()); // SDL SURFACE if(graphic_mode == SDL_GRAPHIC) { SDL_SetAlpha(_surface, SDL_SRCALPHA|SDL_RLEACCEL , _opacity); #ifdef WITH_OPENGL _wreal = w; _hreal = h; _tex_num = 0; #endif } #ifdef WITH_OPENGL // GL SURFACE else { SDL_SetAlpha(_surface, SDL_RLEACCEL , _opacity); // Checking for max texture size if (!CheckTexSize(_surface)) throw Exception("Surface", "Max texture size exceeded\n"); // Setting real texture dimensions to be power-of-two _wreal = (int)powf(2.0, ceilf(logf((float)_surface->w)/logf(2.0f))); _hreal = (int)powf(2.0, ceilf(logf((float)_surface->h)/logf(2.0f))); if (!Surf2Tex(_surface)) throw Exception("Surface", "Texture not created, image format unknown\n"); } _need_tex_update = false; #endif _graphic_mode = graphic_mode; } #ifdef WITH_OPENGL // check for max texture size exceeding bool graphic::Surface::CheckTexSize(SDL_Surface * surface) { GLint max_size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size); if ((surface->w > max_size) || (surface->h > max_size)) return false; else return true; } // make a texture from the class surface bool graphic::Surface::Surf2Tex(SDL_Surface * surface) { SDL_Surface * surf; Uint32 rmask, gmask, bmask, amask; int xpad, ypad; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif surf = SDL_CreateRGBSurface(SDL_SWSURFACE, _wreal, _hreal, 32, rmask, gmask, bmask, amask); if(surf == NULL) return false; // NOT POT if(_wreal != surface->w || _hreal != surface->h) { xpad = (_wreal - surface->w)/2; ypad = (_hreal - surface->h)/2; } // POT else { xpad = ypad = 0; } SDL_Rect dst = {xpad, ypad, surface->w, surface->h}; SDL_BlitSurface(surface, NULL, surf, &dst); glGenTextures(1, &_tex_num); glBindTexture(GL_TEXTURE_2D, _tex_num); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); // Free temporary surface SDL_FreeSurface(surf); return true; } // texture update from backup surface void graphic::Surface::UpdateTex() { if(_graphic_mode == GL_GRAPHIC) { glDeleteTextures(1, &_tex_num); if (!Surf2Tex(_surface)) throw Exception("Surface", "Texture not created, image format unknown\n"); // no more updates required _need_tex_update = false; } } // change the graphic implementation at runtime (SDL/GL) void graphic::Surface::ChangeGraphicMode(bool graphic_mode) { // GL SURFACE if(graphic_mode == GL_GRAPHIC && _graphic_mode != GL_GRAPHIC) { SDL_SetAlpha(_surface, SDL_RLEACCEL, _opacity); // Checking for max texture size if (!CheckTexSize(_surface)) throw Exception("Surface", "Max texture size exceeded\n"); // Setting real texture dimensions to be power-of-two _wreal = (int)powf(2.0, ceilf(logf((float)_surface->w)/logf(2.0f))); _hreal = (int)powf(2.0, ceilf(logf((float)_surface->h)/logf(2.0f))); if (!Surf2Tex(_surface)) throw Exception("Surface", "Texture not created, image format unknown\n"); } // SDL SURFACE else if(_graphic_mode != SDL_GRAPHIC) { glDeleteTextures(1, &_tex_num); SDL_SetAlpha(_surface, SDL_SRCALPHA|SDL_RLEACCEL, _opacity); _wreal = _rectangle->w; _hreal = _rectangle->h; _tex_num = 0; } _graphic_mode = graphic_mode; } #endif // fill the surface with a color void graphic::Surface::Fill(SDL_Color & color) { SDL_FillRect(_surface, NULL, SDL_MapRGB(_surface->format, color.r, color.g, color.b)); #ifdef WITH_OPENGL // GL SURFACE if(_graphic_mode == GL_GRAPHIC) _need_tex_update = true; #endif } // fill part of the surface with a color void graphic::Surface::Fill(SDL_Color & color, SDL_Rect & rect) { // use a temporary rectangle to avoid data loss SDL_Rect frect; frect.x = rect.x; frect.y = rect.y; frect.w = rect.w; frect.h = rect.h; // fill SDL_FillRect(_surface, &frect, SDL_MapRGB(_surface->format, color.r, color.g, color.b)); #ifdef WITH_OPENGL // GL SURFACE if(_graphic_mode == GL_GRAPHIC) _need_tex_update = true; #endif } // set the opacity of the surface void graphic::Surface::SetOpacity(Uint8 opacity) { // SDL SURFACE if(_graphic_mode == SDL_GRAPHIC) { Uint8 old_opacity = _opacity; // RGB surface if(_surface->format->Amask == 0) // set the opacity SDL_SetAlpha(_surface, SDL_SRCALPHA|SDL_RLEACCEL , opacity); // RGBA surface else { Uint8 r, g, b, a; // ratio needed when old_opacity < opacity int ratio = opacity / old_opacity; // rest needed when old_opacity < opacity int rest = opacity % old_opacity; if(SDL_MUSTLOCK(_surface)) SDL_LockSurface(_surface); for(int x = 0; x < _surface->w; x++) { for(int y = 0; y < _surface->h; y++) { // get alpha value of the pixel SDL_GetRGBA(GetPixel(x, y), _surface->format, &r, &g, &b, &a); // reduce the opacity if(old_opacity > opacity) a = (a * opacity) / old_opacity; // increase the opacity else if(old_opacity < opacity) { a = a * ratio; // check if a + rest is bigger than the max value if((a + rest) > SDL_ALPHA_OPAQUE) a = SDL_ALPHA_OPAQUE; // add rest just if a is not really small (!= 0) else if(a > rest && rest <= MAX_REST) a += rest; } SetPixel(x, y, SDL_MapRGBA(_surface->format, r, g, b, a)); } } if(SDL_MUSTLOCK(_surface)) SDL_UnlockSurface(_surface); } } _opacity = opacity; } // get data from the pixel at (x,y) - NEED Lock/Unlock Uint32 graphic::Surface::GetPixel(Sint16 x, Sint16 y) { int bpp = _surface->format->BytesPerPixel; // p is the address of the pixel we want to retrieve Uint8 * p = (Uint8 *)_surface->pixels + y * _surface->pitch + x * bpp; switch(bpp) { case 1: return *p; case 2: return *(Uint16 *)p; case 3: if(SDL_BYTEORDER == SDL_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 *)p; default: return 0; } } // set data of the pixel at (x,y) - NEED Lock/Unlock void graphic::Surface::SetPixel(Sint16 x, Sint16 y, Uint32 data) { int bpp = _surface->format->BytesPerPixel; // p is the address to the pixel we want to set Uint8 *p = (Uint8 *)_surface->pixels + y * _surface->pitch + x * bpp; switch(bpp) { case 1: *p = data; break; case 2: *(Uint16 *)p = data; break; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (data >> 16) & 0xff; p[1] = (data >> 8) & 0xff; p[2] = data & 0xff; } else { p[0] = data & 0xff; p[1] = (data >> 8) & 0xff; p[2] = (data >> 16) & 0xff; } break; case 4: *(Uint32 *)p = data; break; default: break; } #ifdef WITH_OPENGL // GL SURFACE if(_graphic_mode == GL_GRAPHIC) _need_tex_update = true; #endif } // return Alpha value of a pixel at (x,y) - NEED Lock/Unlock Uint8 graphic::Surface::GetAlpha(Sint16 x, Sint16 y) { Uint8 r, g, b, a; SDL_GetRGBA(GetPixel(x, y), _surface->format, &r, &g, &b, &a); return a; }