renderbackendsdl.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2011 by the FIFE team                              *
00003  *   http://www.fifengine.net                                              *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 
00024 // 3rd party library includes
00025 #include <SDL.h>
00026 
00027 // FIFE includes
00028 // These includes are split up in two parts, separated by one empty line
00029 // First block: files included from the FIFE root src directory
00030 // Second block: files included from the same folder
00031 #include "util/base/exception.h"
00032 #include "util/math/fife_math.h"
00033 #include "util/log/logger.h"
00034 #include "video/devicecaps.h"
00035 
00036 #include "renderbackendsdl.h"
00037 #include "sdlimage.h"
00038 #include "SDL_image.h"
00039 #include "SDL_getenv.h"
00040 
00041 namespace FIFE {
00042     static Logger _log(LM_VIDEO);
00043 
00044     RenderBackendSDL::RenderBackendSDL(const SDL_Color& colorkey) :
00045         RenderBackend(colorkey){
00046     }
00047 
00048     RenderBackendSDL::~RenderBackendSDL() {
00049         deinit();
00050     }
00051 
00052     const std::string& RenderBackendSDL::getName() const {
00053         static std::string backend_name = "SDL";
00054         return backend_name;
00055     }
00056 
00057     void RenderBackendSDL::init(const std::string& driver) {
00058         char* buf;
00059         if (driver != "") {
00060             std::string envVar = std::string("SDL_VIDEODRIVER=") + driver;
00061             buf = const_cast<char*>(envVar.c_str());
00062             putenv(buf);
00063         }
00064 
00065         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
00066             throw SDLException(SDL_GetError());
00067 
00068         SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // temporary hack
00069     }
00070 
00071     void RenderBackendSDL::clearBackBuffer() {
00072         SDL_Rect rect;
00073         rect.x = 0;
00074         rect.y = 0;
00075         rect.w = getWidth();
00076         rect.h = getHeight();
00077         SDL_SetClipRect(m_screen, &rect);
00078         SDL_FillRect(m_screen, 0, 0x00);
00079     }
00080 
00081     void RenderBackendSDL::createMainScreen(const ScreenMode& mode, const std::string& title, const std::string& icon){
00082         if(icon != "") {
00083             SDL_Surface *img = IMG_Load(icon.c_str());
00084             if(img != NULL) {
00085                 SDL_WM_SetIcon(img, 0);
00086                 SDL_FreeSurface(img);
00087             }
00088         }
00089 
00090         setScreenMode(mode);
00091         SDL_WM_SetCaption(title.c_str(), 0);
00092     }
00093 
00094     void RenderBackendSDL::setScreenMode(const ScreenMode& mode) {
00095         uint16_t width = mode.getWidth();
00096         uint16_t height = mode.getHeight();
00097         uint16_t bitsPerPixel = mode.getBPP();
00098         bool fs = mode.isFullScreen();
00099         uint32_t flags = mode.getSDLFlags();
00100 
00101         if (bitsPerPixel != 0) {
00102             uint16_t bpp = SDL_VideoModeOK(width, height, bitsPerPixel, flags);
00103             if (!bpp){
00104                 throw SDLException("Selected video mode not supported!");
00105             }
00106         }
00107 
00108         if(m_screen) {
00109             SDL_FreeSurface(m_screen);
00110         }
00111         m_screen = SDL_SetVideoMode(width, height, bitsPerPixel, flags);
00112         if( !m_screen ) {
00113             throw SDLException("Unable to set video mode selected!");
00114         }
00115         m_target = m_screen;
00116 
00117         FL_LOG(_log, LMsg("RenderBackendSDL")
00118             << "Videomode " << width << "x" << height
00119             << " at " << int32_t(m_screen->format->BitsPerPixel) << " bpp");
00120         
00121         m_rgba_format = *(m_screen->format);
00122         m_rgba_format.Rmask = RMASK;
00123         m_rgba_format.Gmask = GMASK;
00124         m_rgba_format.Bmask = BMASK;
00125         m_rgba_format.Amask = AMASK;
00126         
00127         //update the screen mode with the actual flags used
00128         m_screenMode = ScreenMode(width,
00129                                   height,
00130                                   bitsPerPixel,
00131                                   m_screen->flags);
00132     }
00133 
00134     void RenderBackendSDL::startFrame() {
00135         RenderBackend::startFrame();
00136     }
00137 
00138     void RenderBackendSDL::endFrame() {
00139         SDL_Flip(m_screen);
00140         RenderBackend::endFrame();
00141     }
00142 
00143     Image* RenderBackendSDL::createImage(IResourceLoader* loader) {
00144         return new SDLImage(loader);
00145     }
00146 
00147     Image* RenderBackendSDL::createImage(const std::string& name, IResourceLoader* loader) {
00148         return new SDLImage(name, loader);
00149     }
00150 
00151     Image* RenderBackendSDL::createImage(SDL_Surface* surface) {
00152         return new SDLImage(surface);
00153     }
00154 
00155     Image* RenderBackendSDL::createImage(const std::string& name, SDL_Surface* surface) {
00156         return new SDLImage(name, surface);
00157     }
00158 
00159     Image* RenderBackendSDL::createImage(const uint8_t* data, uint32_t width, uint32_t height) {
00160         return new SDLImage(data, width, height);
00161     }
00162 
00163     Image* RenderBackendSDL::createImage(const std::string& name, const uint8_t* data, uint32_t width, uint32_t height) {
00164         return new SDLImage(name, data, width, height);
00165     }
00166 
00167     void RenderBackendSDL::setLightingModel(uint32_t lighting) {
00168         SDLException("Lighting not available under SDL");
00169     }
00170 
00171     uint32_t RenderBackendSDL::getLightingModel() const {
00172         return 0;
00173     }
00174 
00175     void RenderBackendSDL::setLighting(float red, float green, float blue) {
00176     }
00177 
00178     void RenderBackendSDL::resetLighting() {
00179     }
00180 
00181     void RenderBackendSDL::resetStencilBuffer(uint8_t buffer) {
00182     }
00183 
00184     void RenderBackendSDL::changeBlending(int32_t scr, int32_t dst){
00185     }
00186 
00187     void RenderBackendSDL::renderVertexArrays() {
00188     }
00189 
00190     void RenderBackendSDL::addImageToArray(uint32_t id, const Rect& rec, float const* st, uint8_t alpha, uint8_t const* rgb) {
00191     }
00192 
00193     void RenderBackendSDL::changeRenderInfos(uint16_t elements, int32_t src, int32_t dst, bool light, bool stentest, uint8_t stenref, GLConstants stenop, GLConstants stenfunc) {
00194     }
00195 
00196     bool RenderBackendSDL::putPixel(int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00197         return Image::putPixel(m_target, x, y, r, g, b, a);
00198     }
00199 
00200     void RenderBackendSDL::drawLine(const Point& p1, const Point& p2, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00201         // Draw a line with Bresenham, imitated from guichan
00202         int32_t x1 = p1.x;
00203         int32_t x2 = p2.x;
00204         int32_t y1 = p1.y;
00205         int32_t y2 = p2.y;
00206         int32_t dx = ABS(x2 - x1);
00207         int32_t dy = ABS(y2 - y1);
00208 
00209         if (dx > dy) {
00210             if (x1 > x2) {
00211                 // swap x1, x2
00212                 x1 ^= x2;
00213                 x2 ^= x1;
00214                 x1 ^= x2;
00215 
00216                 // swap y1, y2
00217                 y1 ^= y2;
00218                 y2 ^= y1;
00219                 y1 ^= y2;
00220             }
00221 
00222             if (y1 < y2) {
00223                 int32_t y = y1;
00224                 int32_t p = 0;
00225 
00226                 for (int32_t x = x1; x <= x2; x++) {
00227                     putPixel(x, y, r, g, b, a);
00228                     p += dy;
00229                     if (p * 2 >= dx) {
00230                         y++;
00231                         p -= dx;
00232                     }
00233                 }
00234             }
00235             else {
00236                 int32_t y = y1;
00237                 int32_t p = 0;
00238 
00239                 for (int32_t x = x1; x <= x2; x++) {
00240                     putPixel(x, y, r, g, b, a);
00241 
00242                     p += dy;
00243                     if (p * 2 >= dx) {
00244                         y--;
00245                         p -= dx;
00246                     }
00247                 }
00248             }
00249         }
00250         else {
00251             if (y1 > y2) {
00252                 // swap y1, y2
00253                 y1 ^= y2;
00254                 y2 ^= y1;
00255                 y1 ^= y2;
00256 
00257                 // swap x1, x2
00258                 x1 ^= x2;
00259                 x2 ^= x1;
00260                 x1 ^= x2;
00261             }
00262 
00263             if (x1 < x2) {
00264                 int32_t x = x1;
00265                 int32_t p = 0;
00266 
00267                 for (int32_t y = y1; y <= y2; y++) {
00268                     putPixel(x, y, r, g, b, a);
00269                     p += dx;
00270                     if (p * 2 >= dy) {
00271                         x++;
00272                         p -= dy;
00273                     }
00274                 }
00275             }
00276             else {
00277                 int32_t x = x1;
00278                 int32_t p = 0;
00279 
00280                 for (int32_t y = y1; y <= y2; y++) {
00281                     putPixel(x, y, r, g, b, a);
00282                     p += dx;
00283                     if (p * 2 >= dy) {
00284                         x--;
00285                         p -= dy;
00286                     }
00287                 }
00288             }
00289         }
00290     }
00291 
00292     void RenderBackendSDL::drawTriangle(const Point& p1, const Point& p2, const Point& p3, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00293         drawLine(p1, p2, r, g, b, a);
00294         drawLine(p2, p3, r, g, b, a);
00295         drawLine(p3, p1, r, g, b, a);
00296     }
00297 
00298     void RenderBackendSDL::drawRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00299         Point p1, p2, p3, p4;
00300 
00301         p1.x = p.x;
00302         p1.y = p.y;
00303         p2.x = p.x+w;
00304         p2.y = p.y;
00305         p3.x = p.x+w;
00306         p3.y = p.y+h;
00307         p4.x = p.x;
00308         p4.y = p.y+h;
00309 
00310         drawLine(p1, p2, r, g, b, a);
00311         drawLine(p2, p3, r, g, b, a);
00312         drawLine(p3, p4, r, g, b, a);
00313         drawLine(p4, p1, r, g, b, a);
00314     }
00315 
00316     void RenderBackendSDL::fillRectangle(const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00317         SDL_Rect rect;
00318         rect.x = p.x;
00319         rect.y = p.y;
00320         rect.w = w;
00321         rect.h = h;
00322 
00323         Uint32 color = SDL_MapRGBA(m_target->format, r, g, b, a);
00324         SDL_FillRect(m_target, &rect, color);
00325     }
00326 
00327     void RenderBackendSDL::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
00328         fillRectangle(p1, static_cast<uint16_t>(p3.x-p1.x), static_cast<uint16_t>(p3.y-p1.y), r, g, b, a);
00329     }
00330 
00331     void RenderBackendSDL::drawVertex(const Point& p, const uint8_t size, uint8_t r, uint8_t g, uint8_t b, uint8_t a){
00332         Point p1 = Point(p.x-size, p.y+size);
00333         Point p2 = Point(p.x+size, p.y+size);
00334         Point p3 = Point(p.x+size, p.y-size);
00335         Point p4 = Point(p.x-size, p.y-size);
00336 
00337         drawLine(p1, p2, r, g, b, a);
00338         drawLine(p2, p3, r, g, b, a);
00339         drawLine(p3, p4, r, g, b, a);
00340         drawLine(p4, p1, r, g, b, a);
00341     }
00342 
00343     void RenderBackendSDL::drawLightPrimitive(const Point& p, uint8_t intensity, float radius, int32_t subdivisions, float xstretch, float ystretch, uint8_t red, uint8_t green, uint8_t blue) {
00344     }
00345 
00346     void RenderBackendSDL::captureScreen(const std::string& filename) {
00347         if(m_screen) {
00348             const uint32_t swidth = getWidth();
00349             const uint32_t sheight = getHeight();
00350 
00351             SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, sheight, 24,
00352                 RMASK, GMASK, BMASK, NULLMASK);
00353 
00354             if(!surface) {
00355                 return;
00356             }
00357             
00358             SDL_BlitSurface(m_screen, NULL, surface, NULL);
00359 
00360             Image::saveAsPng(filename, *surface);
00361             SDL_FreeSurface(surface);
00362         }
00363     }
00364 
00365     void RenderBackendSDL::captureScreen(const std::string& filename, uint32_t width, uint32_t height) {
00366         if(m_screen) {
00367             const uint32_t swidth = getWidth();
00368             const uint32_t sheight = getHeight();
00369             const bool same_size = (width == swidth && height == sheight);
00370 
00371             if (width < 1 || height < 1) {
00372                 return;
00373             }
00374 
00375             if (same_size) {
00376                 captureScreen(filename);
00377                 return;
00378             }
00379             // create source surface
00380             SDL_Surface* src = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth, sheight, 32,
00381                 RMASK, GMASK, BMASK, AMASK);
00382 
00383             if(!src) {
00384                 return;
00385             }
00386             // copy screen suface to source surface
00387             SDL_BlitSurface(m_screen, NULL, src, NULL);
00388             // create destination surface
00389             SDL_Surface* dst = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32,
00390                 RMASK, GMASK, BMASK, AMASK);
00391 
00392             uint32_t* src_pointer = static_cast<uint32_t*>(src->pixels);
00393             uint32_t* src_help_pointer = src_pointer;
00394             uint32_t* dst_pointer = static_cast<uint32_t*>(dst->pixels);
00395 
00396             int32_t x, y, *sx_ca, *sy_ca;
00397             int32_t sx = static_cast<int32_t>(0xffff * src->w / dst->w);
00398             int32_t sy = static_cast<int32_t>(0xffff * src->h / dst->h);
00399             int32_t sx_c = 0;
00400             int32_t sy_c = 0;
00401 
00402             // Allocates memory and calculates row wide&height
00403             int32_t* sx_a = new int32_t[dst->w + 1];
00404             sx_ca = sx_a;
00405             for (x = 0; x <= dst->w; x++) {
00406                 *sx_ca = sx_c;
00407                 sx_ca++;
00408                 sx_c &= 0xffff;
00409                 sx_c += sx;
00410             }
00411 
00412             int32_t* sy_a = new int32_t[dst->h + 1];
00413             sy_ca = sy_a;
00414             for (y = 0; y <= dst->h; y++) {
00415                 *sy_ca = sy_c;
00416                 sy_ca++;
00417                 sy_c &= 0xffff;
00418                 sy_c += sy;
00419             }
00420             sy_ca = sy_a;
00421 
00422             // Transfers the image data
00423 
00424             if (SDL_MUSTLOCK(src)) {
00425                 SDL_LockSurface(src);
00426             }
00427 
00428             if (SDL_MUSTLOCK(dst)) {
00429                 SDL_LockSurface(dst);
00430             }
00431 
00432             for (y = 0; y < dst->h; y++) {
00433                 src_pointer = src_help_pointer;
00434                 sx_ca = sx_a;
00435                 for (x = 0; x < dst->w; x++) {
00436                     *dst_pointer = *src_pointer;
00437                     sx_ca++;
00438                     src_pointer += (*sx_ca >> 16);
00439                     dst_pointer++;
00440                 }
00441                 sy_ca++;
00442                 src_help_pointer = (uint32_t*)((uint8_t*)src_help_pointer + (*sy_ca >> 16) * src->pitch);
00443             }
00444 
00445             if (SDL_MUSTLOCK(dst)) {
00446                 SDL_UnlockSurface(dst);
00447             }
00448             if (SDL_MUSTLOCK(src)) {
00449                 SDL_UnlockSurface(src);
00450             }
00451 
00452             Image::saveAsPng(filename, *dst);
00453 
00454             // Free memory
00455             SDL_FreeSurface(src);
00456             SDL_FreeSurface(dst);
00457             delete[] sx_a;
00458             delete[] sy_a;
00459         }
00460     }
00461 
00462     void RenderBackendSDL::setClipArea(const Rect& cliparea, bool clear) {
00463         SDL_Rect rect;
00464         rect.x = cliparea.x;
00465         rect.y = cliparea.y;
00466         rect.w = cliparea.w;
00467         rect.h = cliparea.h;
00468         SDL_SetClipRect(m_target, &rect);
00469         if (clear) {
00470             uint32_t color = 0;
00471             if (m_isbackgroundcolor) {
00472                 color = SDL_MapRGB(m_target->format, m_backgroundcolor.r, m_backgroundcolor.g, m_backgroundcolor.b);
00473             }
00474             SDL_FillRect(m_target, &rect, color);
00475         }
00476     }
00477 
00478     void RenderBackendSDL::attachRenderTarget(ImagePtr& img, bool discard) {
00479         m_target = img->getSurface();
00480         if (discard) {
00481             setClipArea(img->getArea(), true);
00482         }
00483     }
00484 
00485     void RenderBackendSDL::detachRenderTarget(){
00486         m_target = m_screen;
00487     }
00488 }