src/video/sdl/sdl_renderer.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "video/sdl/sdl_renderer.hpp"
00018 
00019 #include "video/drawing_request.hpp"
00020 #include "video/sdl/sdl_surface_data.hpp"
00021 #include "video/sdl/sdl_texture.hpp"
00022 
00023 #include <iomanip>
00024 #include <iostream>
00025 #include <physfs.h>
00026 #include <sstream>
00027 #include <stdexcept>
00028 
00029 namespace {
00030 
00031 SDL_Surface *apply_alpha(SDL_Surface *src, float alpha_factor)
00032 {
00033   // FIXME: This is really slow
00034   assert(src->format->Amask);
00035   int alpha = (int) (alpha_factor * 256);
00036   SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
00037   int bpp = dst->format->BytesPerPixel;
00038   if(SDL_MUSTLOCK(src))
00039   {
00040     SDL_LockSurface(src);
00041   }
00042   if(SDL_MUSTLOCK(dst))
00043   {
00044     SDL_LockSurface(dst);
00045   }
00046   for(int y = 0;y < dst->h;y++) {
00047     for(int x = 0;x < dst->w;x++) {
00048       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00049       Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
00050       Uint32 mapped = 0;
00051       switch(bpp) {
00052         case 1:
00053           mapped = *srcpixel;
00054           break;
00055         case 2:
00056           mapped = *(Uint16 *)srcpixel;
00057           break;
00058         case 3:
00059 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00060           mapped |= srcpixel[0] << 16;
00061           mapped |= srcpixel[1] << 8;
00062           mapped |= srcpixel[2] << 0;
00063 #else
00064           mapped |= srcpixel[0] << 0;
00065           mapped |= srcpixel[1] << 8;
00066           mapped |= srcpixel[2] << 16;
00067 #endif
00068           break;
00069         case 4:
00070           mapped = *(Uint32 *)srcpixel;
00071           break;
00072       }
00073       Uint8 r, g, b, a;
00074       SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
00075       mapped = SDL_MapRGBA(dst->format, r, g, b, (a * alpha) >> 8);
00076       switch(bpp) {
00077         case 1:
00078           *dstpixel = mapped;
00079           break;
00080         case 2:
00081           *(Uint16 *)dstpixel = mapped;
00082           break;
00083         case 3:
00084 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00085           dstpixel[0] = (mapped >> 16) & 0xff;
00086           dstpixel[1] = (mapped >> 8) & 0xff;
00087           dstpixel[2] = (mapped >> 0) & 0xff;
00088 #else
00089           dstpixel[0] = (mapped >> 0) & 0xff;
00090           dstpixel[1] = (mapped >> 8) & 0xff;
00091           dstpixel[2] = (mapped >> 16) & 0xff;
00092 #endif
00093           break;
00094         case 4:
00095           *(Uint32 *)dstpixel = mapped;
00096           break;
00097       }
00098     }
00099   }
00100   if(SDL_MUSTLOCK(dst))
00101   {
00102     SDL_UnlockSurface(dst);
00103   }
00104   if(SDL_MUSTLOCK(src))
00105   {
00106     SDL_UnlockSurface(src);
00107   }
00108   return dst;
00109 }
00110 
00111 } // namespace
00112 
00113 SDLRenderer::SDLRenderer() :
00114   screen(),
00115   numerator(),
00116   denominator()
00117 {
00118   Renderer::instance_ = this;
00119 
00120   const SDL_VideoInfo *info = SDL_GetVideoInfo();
00121   log_info << "Hardware surfaces are " << (info->hw_available ? "" : "not ") << "available." << std::endl;
00122   log_info << "Hardware to hardware blits are " << (info->blit_hw ? "" : "not ") << "accelerated." << std::endl;
00123   log_info << "Hardware to hardware blits with colorkey are " << (info->blit_hw_CC ? "" : "not ") << "accelerated." << std::endl;
00124   log_info << "Hardware to hardware blits with alpha are " << (info->blit_hw_A ? "" : "not ") << "accelerated." << std::endl;
00125   log_info << "Software to hardware blits are " << (info->blit_sw ? "" : "not ") << "accelerated." << std::endl;
00126   log_info << "Software to hardware blits with colorkey are " << (info->blit_sw_CC ? "" : "not ") << "accelerated." << std::endl;
00127   log_info << "Software to hardware blits with alpha are " << (info->blit_sw_A ? "" : "not ") << "accelerated." << std::endl;
00128   log_info << "Color fills are " << (info->blit_fill ? "" : "not ") << "accelerated." << std::endl;
00129 
00130   int flags = SDL_SWSURFACE | SDL_ANYFORMAT;
00131   if(g_config->use_fullscreen)
00132     flags |= SDL_FULLSCREEN;
00133     
00134   int width  = 800; //FIXME: config->screenwidth;
00135   int height = 600; //FIXME: config->screenheight;
00136 
00137   screen = SDL_SetVideoMode(width, height, 0, flags);
00138   if(screen == 0) {
00139     std::stringstream msg;
00140     msg << "Couldn't set video mode (" << width << "x" << height
00141         << "): " << SDL_GetError();
00142     throw std::runtime_error(msg.str());
00143   }
00144 
00145   numerator   = 1;
00146   denominator = 1;
00147   /* FIXME: 
00148      float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
00149      float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
00150      if(xfactor < yfactor)
00151      {
00152      numerator = config->screenwidth;
00153      denominator = SCREEN_WIDTH;
00154      }
00155      else
00156      {
00157      numerator = config->screenheight;
00158      denominator = SCREEN_HEIGHT;
00159      }
00160   */
00161   if(texture_manager == 0)
00162     texture_manager = new TextureManager();
00163 }
00164 
00165 SDLRenderer::~SDLRenderer()
00166 {
00167 }
00168 
00169 void
00170 SDLRenderer::draw_surface(const DrawingRequest& request)
00171 {
00172   //FIXME: support parameters request.alpha, request.angle, request.blend
00173   const Surface* surface = (const Surface*) request.request_data;
00174   boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
00175   SDLSurfaceData *surface_data = reinterpret_cast<SDLSurfaceData *>(surface->get_surface_data());
00176 
00177   DrawingEffect effect = request.drawing_effect;
00178   if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
00179 
00180   SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
00181 
00182   // get and check SDL_Surface
00183   if (transform == 0) {
00184     std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
00185     return;
00186   }
00187 
00188   SDL_Rect *src_rect = surface_data->get_src_rect(effect);
00189   SDL_Rect dst_rect;
00190   dst_rect.x = (int) request.pos.x * numerator / denominator;
00191   dst_rect.y = (int) request.pos.y * numerator / denominator;
00192 
00193   Uint8 alpha = 0;
00194   if(request.alpha != 1.0)
00195   {
00196     if(!transform->format->Amask)
00197     {
00198       if(transform->flags & SDL_SRCALPHA)
00199       {
00200         alpha = transform->format->alpha;
00201       }
00202       else
00203       {
00204         alpha = 255;
00205       }
00206       SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
00207     }
00208     /*else
00209       {
00210       transform = apply_alpha(transform, request.alpha);
00211       }*/
00212   }
00213 
00214   SDL_BlitSurface(transform, src_rect, screen, &dst_rect);
00215 
00216   if(request.alpha != 1.0)
00217   {
00218     if(!transform->format->Amask)
00219     {
00220       if(alpha == 255)
00221       {
00222         SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
00223       }
00224       else
00225       {
00226         SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
00227       }
00228     }
00229     /*else
00230       {
00231       SDL_FreeSurface(transform);
00232       }*/
00233   }
00234 }
00235 
00236 void
00237 SDLRenderer::draw_surface_part(const DrawingRequest& request)
00238 {
00239   const SurfacePartRequest* surfacepartrequest
00240     = (SurfacePartRequest*) request.request_data;
00241 
00242   const Surface* surface = surfacepartrequest->surface;
00243   boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
00244 
00245   DrawingEffect effect = request.drawing_effect;
00246   if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
00247 
00248   SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
00249 
00250   // get and check SDL_Surface
00251   if (transform == 0) {
00252     std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
00253     return;
00254   }
00255 
00256   int ox, oy;
00257   if (effect == HORIZONTAL_FLIP)
00258   {
00259     ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
00260   }
00261   else
00262   {
00263     ox = surface->get_x();
00264   }
00265   if (effect == VERTICAL_FLIP)
00266   {
00267     oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
00268   }
00269   else
00270   {
00271     oy = surface->get_y();
00272   }
00273 
00274   SDL_Rect src_rect;
00275   src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
00276   src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
00277   src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
00278   src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
00279 
00280   SDL_Rect dst_rect;
00281   dst_rect.x = (int) request.pos.x * numerator / denominator;
00282   dst_rect.y = (int) request.pos.y * numerator / denominator;
00283 
00284   Uint8 alpha = 0;
00285   if(request.alpha != 1.0)
00286   {
00287     if(!transform->format->Amask)
00288     {
00289       if(transform->flags & SDL_SRCALPHA)
00290       {
00291         alpha = transform->format->alpha;
00292       }
00293       else
00294       {
00295         alpha = 255;
00296       }
00297       SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
00298     }
00299     /*else
00300       {
00301       transform = apply_alpha(transform, request.alpha);
00302       }*/
00303   }
00304 
00305   SDL_BlitSurface(transform, &src_rect, screen, &dst_rect);
00306 
00307   if(request.alpha != 1.0)
00308   {
00309     if(!transform->format->Amask)
00310     {
00311       if(alpha == 255)
00312       {
00313         SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
00314       }
00315       else
00316       {
00317         SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
00318       }
00319     }
00320     /*else
00321       {
00322       SDL_FreeSurface(transform);
00323       }*/
00324   }
00325 }
00326 
00327 void
00328 SDLRenderer::draw_gradient(const DrawingRequest& request)
00329 {
00330   const GradientRequest* gradientrequest 
00331     = (GradientRequest*) request.request_data;
00332   const Color& top = gradientrequest->top;
00333   const Color& bottom = gradientrequest->bottom;
00334 
00335   for(int y = 0;y < screen->h;++y)
00336   {
00337     Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255);
00338     Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255);
00339     Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255);
00340     Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255);
00341     Uint32 color = SDL_MapRGB(screen->format, r, g, b);
00342 
00343     SDL_Rect rect;
00344     rect.x = 0;
00345     rect.y = y;
00346     rect.w = screen->w;
00347     rect.h = 1;
00348 
00349     if(a == SDL_ALPHA_OPAQUE) {
00350       SDL_FillRect(screen, &rect, color);
00351     } else if(a != SDL_ALPHA_TRANSPARENT) {
00352       SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
00353 
00354       SDL_FillRect(temp, 0, color);
00355       SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
00356       SDL_BlitSurface(temp, 0, screen, &rect);
00357       SDL_FreeSurface(temp);
00358     }
00359   }
00360 }
00361 
00362 void
00363 SDLRenderer::draw_filled_rect(const DrawingRequest& request)
00364 {
00365   const FillRectRequest* fillrectrequest
00366     = (FillRectRequest*) request.request_data;
00367 
00368   SDL_Rect rect;
00369   rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH;
00370   rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT;
00371   rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH;
00372   rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT;
00373   if((rect.w == 0) || (rect.h == 0)) {
00374     return;
00375   }
00376   Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
00377   Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
00378   Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
00379   Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
00380   Uint32 color = SDL_MapRGB(screen->format, r, g, b);
00381   if(a == SDL_ALPHA_OPAQUE) {
00382     SDL_FillRect(screen, &rect, color);
00383   } else if(a != SDL_ALPHA_TRANSPARENT) {
00384     SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
00385 
00386     SDL_FillRect(temp, 0, color);
00387     SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
00388     SDL_BlitSurface(temp, 0, screen, &rect);
00389     SDL_FreeSurface(temp);
00390   }
00391 }
00392 
00393 void
00394 SDLRenderer::draw_inverse_ellipse(const DrawingRequest&)
00395 {
00396 }
00397 
00398 void 
00399 SDLRenderer::do_take_screenshot()
00400 {
00401   // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
00402 
00403   SDL_Surface *screen = SDL_GetVideoSurface();
00404 
00405   // save screenshot
00406   static const std::string writeDir = PHYSFS_getWriteDir();
00407   static const std::string dirSep = PHYSFS_getDirSeparator();
00408   static const std::string baseName = "screenshot";
00409   static const std::string fileExt = ".bmp";
00410   std::string fullFilename;
00411   for (int num = 0; num < 1000; num++) {
00412     std::ostringstream oss;
00413     oss << baseName;
00414     oss << std::setw(3) << std::setfill('0') << num;
00415     oss << fileExt;
00416     std::string fileName = oss.str();
00417     fullFilename = writeDir + dirSep + fileName;
00418     if (!PHYSFS_exists(fileName.c_str())) {
00419       SDL_SaveBMP(screen, fullFilename.c_str());
00420       log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
00421       return;
00422     }
00423   }
00424   log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
00425 }
00426 
00427 void
00428 SDLRenderer::flip()
00429 {
00430   SDL_Flip(screen);
00431 }
00432 
00433 void
00434 SDLRenderer::resize(int, int)
00435 {
00436     
00437 }
00438 
00439 /* EOF */

Generated on Mon Jun 9 03:38:24 2014 for SuperTux by  doxygen 1.5.1