00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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 }
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;
00135 int height = 600;
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
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
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
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
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
00209
00210
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
00230
00231
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
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
00300
00301
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
00321
00322
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
00402
00403 SDL_Surface *screen = SDL_GetVideoSurface();
00404
00405
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