2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 // Updated by GiBy 2013 for SDL2 <giby_the_kid@yahoo.fr>
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "video/sdl/sdl_renderer.hpp"
20 #include "util/log.hpp"
21 #include "video/drawing_request.hpp"
22 #include "video/sdl/sdl_surface_data.hpp"
23 #include "video/sdl/sdl_texture.hpp"
30 #include "SDL2/SDL_video.h"
34 SDL_Surface *apply_alpha(SDL_Surface *src, float alpha_factor)
36 // FIXME: This is really slow
37 assert(src->format->Amask);
38 int alpha = (int) (alpha_factor * 256);
39 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);
40 int bpp = dst->format->BytesPerPixel;
49 for(int y = 0;y < dst->h;y++) {
50 for(int x = 0;x < dst->w;x++) {
51 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
52 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
59 mapped = *(Uint16 *)srcpixel;
62 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
63 mapped |= srcpixel[0] << 16;
64 mapped |= srcpixel[1] << 8;
65 mapped |= srcpixel[2] << 0;
67 mapped |= srcpixel[0] << 0;
68 mapped |= srcpixel[1] << 8;
69 mapped |= srcpixel[2] << 16;
73 mapped = *(Uint32 *)srcpixel;
77 SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
78 mapped = SDL_MapRGBA(dst->format, r, g, b, (a * alpha) >> 8);
84 *(Uint16 *)dstpixel = mapped;
87 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
88 dstpixel[0] = (mapped >> 16) & 0xff;
89 dstpixel[1] = (mapped >> 8) & 0xff;
90 dstpixel[2] = (mapped >> 0) & 0xff;
92 dstpixel[0] = (mapped >> 0) & 0xff;
93 dstpixel[1] = (mapped >> 8) & 0xff;
94 dstpixel[2] = (mapped >> 16) & 0xff;
98 *(Uint32 *)dstpixel = mapped;
103 if(SDL_MUSTLOCK(dst))
105 SDL_UnlockSurface(dst);
107 if(SDL_MUSTLOCK(src))
109 SDL_UnlockSurface(src);
116 SDLRenderer::SDLRenderer() :
122 Renderer::instance_ = this;
124 // Cannot currently find a way to do this with SDL2
125 //const SDL_VideoInfo *info = SDL_GetVideoInfo();
126 //log_info << "Hardware surfaces are " << (info->hw_available ? "" : "not ") << "available." << std::endl;
127 //log_info << "Hardware to hardware blits are " << (info->blit_hw ? "" : "not ") << "accelerated." << std::endl;
128 //log_info << "Hardware to hardware blits with colorkey are " << (info->blit_hw_CC ? "" : "not ") << "accelerated." << std::endl;
129 //log_info << "Hardware to hardware blits with alpha are " << (info->blit_hw_A ? "" : "not ") << "accelerated." << std::endl;
130 //log_info << "Software to hardware blits are " << (info->blit_sw ? "" : "not ") << "accelerated." << std::endl;
131 //log_info << "Software to hardware blits with colorkey are " << (info->blit_sw_CC ? "" : "not ") << "accelerated." << std::endl;
132 //log_info << "Software to hardware blits with alpha are " << (info->blit_sw_A ? "" : "not ") << "accelerated." << std::endl;
133 //log_info << "Color fills are " << (info->blit_fill ? "" : "not ") << "accelerated." << std::endl;
135 // int flags = SDL_SWSURFACE | SDL_ANYFORMAT;
136 // if(g_config->use_fullscreen)
137 // flags |= SDL_FULLSCREEN;
139 log_info << "creating SDLRenderer" << std::endl;
140 int width = 800; //FIXME: config->screenwidth;
141 int height = 600; //FIXME: config->screenheight;
143 int ret = SDL_CreateWindowAndRenderer(width, height, flags,
147 std::stringstream msg;
148 msg << "Couldn't set video mode (" << width << "x" << height
149 << "): " << SDL_GetError();
150 throw std::runtime_error(msg.str());
152 SDL_SetWindowTitle(window, "SuperTux");
153 if(texture_manager == 0)
154 texture_manager = new TextureManager();
160 float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
161 float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
162 if(xfactor < yfactor)
164 numerator = config->screenwidth;
165 denominator = SCREEN_WIDTH;
169 numerator = config->screenheight;
170 denominator = SCREEN_HEIGHT;
176 SDLRenderer::~SDLRenderer()
178 SDL_DestroyRenderer(renderer);
179 SDL_DestroyWindow(window);
183 SDLRenderer::draw_surface(const DrawingRequest& request)
185 //FIXME: support parameters request.alpha, request.angle, request.blend
186 const Surface* surface = (const Surface*) request.request_data;
187 boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
190 dst_rect.x = request.pos.x;
191 dst_rect.y = request.pos.y;
192 dst_rect.w = sdltexture->get_image_width();
193 dst_rect.h = sdltexture->get_image_height();
196 // FIXME: Use SDL_RenderCopyEx() to handle flipping
199 SDL_RenderCopy(renderer, sdltexture->get_texture(), NULL, &dst_rect);
202 SDLSurfaceData *surface_data = reinterpret_cast<SDLSurfaceData *>(surface->get_surface_data());
204 DrawingEffect effect = request.drawing_effect;
205 if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
207 SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
209 // get and check SDL_Surface
210 if (transform == 0) {
211 std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
215 SDL_Rect *src_rect = surface_data->get_src_rect(effect);
217 dst_rect.x = (int) request.pos.x * numerator / denominator;
218 dst_rect.y = (int) request.pos.y * numerator / denominator;
221 if(request.alpha != 1.0)
223 if(!transform->format->Amask)
225 if(transform->flags & SDL_SRCALPHA)
227 alpha = transform->format->alpha;
233 SDL_SetSurfaceAlphaMod(transform, (Uint8) (request.alpha * alpha));
237 transform = apply_alpha(transform, request.alpha);
241 SDL_BlitSurface(transform, src_rect, screen, &dst_rect);
243 if(request.alpha != 1.0)
245 if(!transform->format->Amask)
249 SDL_SetSurfaceAlphaMod(transform, 0);
253 SDL_SetSurfaceAlphaMod(transform, alpha);
258 SDL_FreeSurface(transform);
265 SDLRenderer::draw_surface_part(const DrawingRequest& request)
267 //FIXME: support parameters request.alpha, request.angle, request.blend
268 const SurfacePartRequest* surface = (const SurfacePartRequest*) request.request_data;
269 const SurfacePartRequest* surfacepartrequest = (SurfacePartRequest*) request.request_data;
271 boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->surface->get_texture());
274 src_rect.x = surfacepartrequest->source.x;
275 src_rect.y = surfacepartrequest->source.y;
276 src_rect.w = surfacepartrequest->size.x;
277 src_rect.h = surfacepartrequest->size.y;
280 dst_rect.x = request.pos.x;
281 dst_rect.y = request.pos.y;
282 dst_rect.w = surfacepartrequest->size.x;
283 dst_rect.h = surfacepartrequest->size.y;
286 // FIXME: Use SDL_RenderCopyEx() to handle flipping
289 SDL_RenderCopy(renderer, sdltexture->get_texture(),
290 &src_rect, &dst_rect);
293 const SurfacePartRequest* surfacepartrequest
294 = (SurfacePartRequest*) request.request_data;
296 const Surface* surface = surfacepartrequest->surface;
297 boost::shared_ptr<SDLTexture> sdltexture = boost::dynamic_pointer_cast<SDLTexture>(surface->get_texture());
299 DrawingEffect effect = request.drawing_effect;
300 if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
302 SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
304 // get and check SDL_Surface
305 if (transform == 0) {
306 std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
311 if (effect == HORIZONTAL_FLIP)
313 ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
317 ox = surface->get_x();
319 if (effect == VERTICAL_FLIP)
321 oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
325 oy = surface->get_y();
329 src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
330 src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
331 src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
332 src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
335 dst_rect.x = (int) request.pos.x * numerator / denominator;
336 dst_rect.y = (int) request.pos.y * numerator / denominator;
339 if(request.alpha != 1.0)
341 if(!transform->format->Amask)
343 if(transform->flags & SDL_SRCALPHA)
345 alpha = transform->format->alpha;
351 SDL_SetSurfaceAlphaMod(transform, (Uint8) (request.alpha * alpha));
355 transform = apply_alpha(transform, request.alpha);
361 SDL_BlitSurface(transform, &src_rect, screen, &dst_rect);
365 if(request.alpha != 1.0)
367 if(!transform->format->Amask)
371 SDL_SetSurfaceAlphaMod(transform, 0);
375 SDL_SetSurfaceAlphaMod(transform, alpha);
380 SDL_FreeSurface(transform);
387 SDLRenderer::draw_gradient(const DrawingRequest& request)
389 const GradientRequest* gradientrequest
390 = (GradientRequest*) request.request_data;
391 const Color& top = gradientrequest->top;
392 const Color& bottom = gradientrequest->bottom;
396 SDL_GetWindowSize(window, &w, &h);
398 // calculate the maximum number of steps needed for the gradient
399 int n = static_cast<int>(std::max(std::max(fabsf(top.red - bottom.red),
400 fabsf(top.green - bottom.green)),
401 std::max(fabsf(top.blue - bottom.blue),
402 fabsf(top.alpha - bottom.alpha))) * 255);
403 for(int i = 0; i < n; ++i)
409 rect.h = (h * (i+1) / n) - rect.y;
411 float p = static_cast<float>(i+1) / static_cast<float>(n);
412 Uint8 r = static_cast<Uint8>(((1.0f - p) * top.red + p * bottom.red) * 255);
413 Uint8 g = static_cast<Uint8>(((1.0f - p) * top.green + p * bottom.green) * 255);
414 Uint8 b = static_cast<Uint8>(((1.0f - p) * top.blue + p * bottom.blue) * 255);
415 Uint8 a = static_cast<Uint8>(((1.0f - p) * top.alpha + p * bottom.alpha) * 255);
417 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
418 SDL_SetRenderDrawColor(renderer, r, g, b, a);
419 SDL_RenderFillRect(renderer, &rect);
423 const GradientRequest* gradientrequest
424 = (GradientRequest*) request.request_data;
425 const Color& top = gradientrequest->top;
426 const Color& bottom = gradientrequest->bottom;
428 for(int y = 0;y < screen->h;++y)
430 Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255);
431 Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255);
432 Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255);
433 Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255);
434 Uint32 color = SDL_MapRGB(screen->format, r, g, b);
442 if(a == SDL_ALPHA_OPAQUE) {
443 SDL_FillRect(screen, &rect, color);
444 } else if(a != SDL_ALPHA_TRANSPARENT) {
445 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);
447 SDL_FillRect(temp, 0, color);
448 SDL_SetSurfaceAlphaMod(temp, a);
449 SDL_BlitSurface(temp, 0, screen, &rect);
450 SDL_FreeSurface(temp);
457 SDLRenderer::draw_filled_rect(const DrawingRequest& request)
459 const FillRectRequest* fillrectrequest
460 = (FillRectRequest*) request.request_data;
463 rect.x = request.pos.x;
464 rect.y = request.pos.y;
465 rect.w = fillrectrequest->size.x;
466 rect.h = fillrectrequest->size.y;
468 if((rect.w != 0) && (rect.h != 0))
470 Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
471 Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
472 Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
473 Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
475 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
476 SDL_SetRenderDrawColor(renderer, r, g, b, a);
477 SDL_RenderFillRect(renderer, &rect);
481 const FillRectRequest* fillrectrequest
482 = (FillRectRequest*) request.request_data;
485 rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH;
486 rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT;
487 rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH;
488 rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT;
489 if((rect.w == 0) || (rect.h == 0)) {
492 Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
493 Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
494 Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
495 Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
496 Uint32 color = SDL_MapRGB(screen->format, r, g, b);
497 if(a == SDL_ALPHA_OPAQUE) {
498 SDL_FillRect(screen, &rect, color);
499 } else if(a != SDL_ALPHA_TRANSPARENT) {
500 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);
502 SDL_FillRect(temp, 0, color);
503 SDL_SetSurfaceAlphaMod(temp, a);
504 SDL_BlitSurface(temp, 0, screen, &rect);
505 SDL_FreeSurface(temp);
511 SDLRenderer::draw_inverse_ellipse(const DrawingRequest& request)
513 const InverseEllipseRequest* ellipse = (InverseEllipseRequest*)request.request_data;
517 SDL_GetWindowSize(window, &window_w, &window_h);
519 float x = request.pos.x;
520 float w = ellipse->size.x;
521 float h = ellipse->size.y;
523 int top = request.pos.y - (h / 2);
525 const int max_slices = 256;
526 SDL_Rect rects[2*max_slices+2];
527 int slices = std::min(static_cast<int>(ellipse->size.y), max_slices);
528 for(int i = 0; i < slices; ++i)
530 float p = ((static_cast<float>(i) + 0.5f) / static_cast<float>(slices)) * 2.0f - 1.0f;
531 int xoff = static_cast<int>(sqrtf(1.0f - p*p) * w / 2);
533 SDL_Rect& left = rects[2*i+0];
534 SDL_Rect& right = rects[2*i+1];
537 left.y = top + (i * h / slices);
539 left.h = (top + ((i+1) * h / slices)) - left.y;
543 right.w = window_w - right.x;
547 SDL_Rect& top_rect = rects[2*slices+0];
548 SDL_Rect& bottom_rect = rects[2*slices+1];
552 top_rect.w = window_w;
556 bottom_rect.y = top + h;
557 bottom_rect.w = window_w;
558 bottom_rect.h = window_h - bottom_rect.y;
560 Uint8 r = static_cast<Uint8>(ellipse->color.red * 255);
561 Uint8 g = static_cast<Uint8>(ellipse->color.green * 255);
562 Uint8 b = static_cast<Uint8>(ellipse->color.blue * 255);
563 Uint8 a = static_cast<Uint8>(ellipse->color.alpha * 255);
565 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
566 SDL_SetRenderDrawColor(renderer, r, g, b, a);
567 SDL_RenderFillRects(renderer, rects, 2*slices+2);
571 SDLRenderer::do_take_screenshot()
573 // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
575 SDL_Surface *screen = SDL_GetWindowSurface(window);
578 static const std::string writeDir = PHYSFS_getWriteDir();
579 static const std::string dirSep = PHYSFS_getDirSeparator();
580 static const std::string baseName = "screenshot";
581 static const std::string fileExt = ".bmp";
582 std::string fullFilename;
583 for (int num = 0; num < 1000; num++) {
584 std::ostringstream oss;
586 oss << std::setw(3) << std::setfill('0') << num;
588 std::string fileName = oss.str();
589 fullFilename = writeDir + dirSep + fileName;
590 if (!PHYSFS_exists(fileName.c_str())) {
591 SDL_SaveBMP(screen, fullFilename.c_str());
592 log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
596 log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
602 SDL_RenderPresent(renderer);
606 SDLRenderer::resize(int, int)