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"
24 #include "video/sdl/sdl_painter.hpp"
31 #include "SDL2/SDL_video.h"
33 SDLRenderer::SDLRenderer() :
39 Renderer::instance_ = this;
42 SDL_GetCurrentDisplayMode(0, &mode);
43 desktop_size = Size(mode.w, mode.h);
45 log_info << "creating SDLRenderer" << std::endl;
46 int width = g_config->window_size.width;
47 int height = g_config->window_size.height;
49 int flags = SDL_WINDOW_RESIZABLE;
50 if(g_config->use_fullscreen)
52 flags |= SDL_WINDOW_FULLSCREEN;
53 width = g_config->fullscreen_size.width;
54 height = g_config->fullscreen_size.height;
58 SCREEN_HEIGHT = height;
62 viewport.w = SCREEN_WIDTH;
63 viewport.h = SCREEN_HEIGHT;
65 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
67 int ret = SDL_CreateWindowAndRenderer(width, height, flags,
71 std::stringstream msg;
72 msg << "Couldn't set video mode (" << width << "x" << height
73 << "): " << SDL_GetError();
74 throw std::runtime_error(msg.str());
77 SDL_RendererInfo info;
78 if (SDL_GetRendererInfo(renderer, &info) != 0)
80 log_warning << "Couldn't get RendererInfo: " << SDL_GetError() << std::endl;
84 log_info << "SDL_Renderer: " << info.name << std::endl;
85 log_info << "SDL_RendererFlags: " << std::endl;
86 if (info.flags & SDL_RENDERER_SOFTWARE) log_info << " SDL_RENDERER_SOFTWARE" << std::endl;
87 if (info.flags & SDL_RENDERER_ACCELERATED) log_info << " SDL_RENDERER_ACCELERATED" << std::endl;
88 if (info.flags & SDL_RENDERER_PRESENTVSYNC) log_info << " SDL_RENDERER_PRESENTVSYNC" << std::endl;
89 if (info.flags & SDL_RENDERER_TARGETTEXTURE) log_info << " SDL_RENDERER_TARGETTEXTURE" << std::endl;
90 log_info << "Texture Formats: " << std::endl;
91 for(size_t i = 0; i < info.num_texture_formats; ++i)
93 log_info << " " << SDL_GetPixelFormatName(info.texture_formats[i]) << std::endl;
95 log_info << "Max Texture Width: " << info.max_texture_width << std::endl;
96 log_info << "Max Texture Height: " << info.max_texture_height << std::endl;
99 if(texture_manager == 0)
100 texture_manager = new TextureManager();
105 SDLRenderer::~SDLRenderer()
107 SDL_DestroyRenderer(renderer);
108 SDL_DestroyWindow(window);
112 SDLRenderer::draw_surface(const DrawingRequest& request)
114 SDLPainter::draw_surface(renderer, request);
118 SDLRenderer::draw_surface_part(const DrawingRequest& request)
120 SDLPainter::draw_surface_part(renderer, request);
124 SDLRenderer::draw_gradient(const DrawingRequest& request)
126 SDLPainter::draw_gradient(renderer, request);
130 SDLRenderer::draw_filled_rect(const DrawingRequest& request)
132 SDLPainter::draw_filled_rect(renderer, request);
136 SDLRenderer::draw_inverse_ellipse(const DrawingRequest& request)
138 SDLPainter::draw_inverse_ellipse(renderer, request);
142 SDLRenderer::do_take_screenshot()
144 // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
147 if (SDL_GetRendererOutputSize(renderer, &width, &height) != 0)
149 log_warning << "SDL_GetRenderOutputSize failed: " << SDL_GetError() << std::endl;
153 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
154 Uint32 rmask = 0xff000000;
155 Uint32 gmask = 0x00ff0000;
156 Uint32 bmask = 0x0000ff00;
157 Uint32 amask = 0x000000ff;
159 Uint32 rmask = 0x000000ff;
160 Uint32 gmask = 0x0000ff00;
161 Uint32 bmask = 0x00ff0000;
162 Uint32 amask = 0xff000000;
164 SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32,
165 rmask, gmask, bmask, amask);
168 log_warning << "SDL_CreateRGBSurface failed: " << SDL_GetError() << std::endl;
172 int ret = SDL_RenderReadPixels(renderer, NULL,
173 SDL_PIXELFORMAT_ABGR8888,
178 log_warning << "SDL_RenderReadPixels failed: " << SDL_GetError() << std::endl;
183 static const std::string writeDir = PHYSFS_getWriteDir();
184 static const std::string dirSep = PHYSFS_getDirSeparator();
185 static const std::string baseName = "screenshot";
186 static const std::string fileExt = ".bmp";
187 std::string fullFilename;
188 for (int num = 0; num < 1000; num++) {
189 std::ostringstream oss;
191 oss << std::setw(3) << std::setfill('0') << num;
193 std::string fileName = oss.str();
194 fullFilename = writeDir + dirSep + fileName;
195 if (!PHYSFS_exists(fileName.c_str())) {
196 SDL_SaveBMP(surface, fullFilename.c_str());
197 log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
201 log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
210 SDL_RenderPresent(renderer);
214 SDLRenderer::resize(int w , int h)
216 g_config->window_size = Size(w, h);
222 SDLRenderer::apply_config()
226 log_info << "Applying Config:"
227 << "\n Desktop: " << desktop_size.width << "x" << desktop_size.height
228 << "\n Window: " << g_config->window_size
229 << "\n FullRes: " << g_config->fullscreen_size
230 << "\n Aspect: " << g_config->aspect_size
231 << "\n Magnif: " << g_config->magnification
235 float target_aspect = static_cast<float>(desktop_size.width) / static_cast<float>(desktop_size.height);
236 if (g_config->aspect_size != Size(0, 0))
238 target_aspect = float(g_config->aspect_size.width) / float(g_config->aspect_size.height);
241 float desktop_aspect = 4.0f / 3.0f; // random default fallback guess
242 if (desktop_size.width != -1 && desktop_size.height != -1)
244 desktop_aspect = float(desktop_size.width) / float(desktop_size.height);
249 // Get the screen width
250 if (g_config->use_fullscreen)
252 screen_size = g_config->fullscreen_size;
253 desktop_aspect = float(screen_size.width) / float(screen_size.height);
257 screen_size = g_config->window_size;
260 if (!g_config->use_fullscreen)
262 SDL_SetWindowFullscreen(window, 0);
266 SDL_DisplayMode mode;
267 mode.format = SDL_PIXELFORMAT_RGB888;
268 mode.w = g_config->fullscreen_size.width;
269 mode.h = g_config->fullscreen_size.height;
270 mode.refresh_rate = g_config->fullscreen_refresh_rate;
273 if (SDL_SetWindowDisplayMode(window, &mode) != 0)
275 log_warning << "failed to set display mode: "
276 << mode.w << "x" << mode.h << "@" << mode.refresh_rate << ": "
277 << SDL_GetError() << std::endl;
281 SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
285 if (target_aspect > 1.0f)
287 SCREEN_WIDTH = static_cast<int>(screen_size.width * (target_aspect / desktop_aspect));
288 SCREEN_HEIGHT = static_cast<int>(screen_size.height);
292 SCREEN_WIDTH = static_cast<int>(screen_size.width);
293 SCREEN_HEIGHT = static_cast<int>(screen_size.height * (target_aspect / desktop_aspect));
296 Size max_size(1280, 800);
297 Size min_size(640, 480);
299 if (g_config->magnification == 0.0f) // Magic value that means 'minfill'
301 float magnification = 1.0f;
303 // This scales SCREEN_WIDTH/SCREEN_HEIGHT so that they never excede
304 // max_size.width/max_size.height resp. min_size.width/min_size.height
305 if (SCREEN_WIDTH > max_size.width || SCREEN_HEIGHT > max_size.height)
307 float scale1 = float(max_size.width)/SCREEN_WIDTH;
308 float scale2 = float(max_size.height)/SCREEN_HEIGHT;
309 magnification = (scale1 < scale2) ? scale1 : scale2;
310 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH * magnification);
311 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * magnification);
313 else if (SCREEN_WIDTH < min_size.width || SCREEN_HEIGHT < min_size.height)
315 float scale1 = float(min_size.width)/SCREEN_WIDTH;
316 float scale2 = float(min_size.height)/SCREEN_HEIGHT;
317 magnification = (scale1 < scale2) ? scale1 : scale2;
318 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH * magnification);
319 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * magnification);
324 viewport.w = screen_size.width;
325 viewport.h = screen_size.height;
327 SDL_RenderSetScale(renderer, 1.0f, 1.0f);
328 SDL_RenderSetViewport(renderer, &viewport);
329 SDL_RenderSetScale(renderer, magnification, magnification);
333 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH / g_config->magnification);
334 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT / g_config->magnification);
336 // This works by adding black borders around the screen to limit
337 // SCREEN_WIDTH/SCREEN_HEIGHT to max_size.width/max_size.height
338 Size new_size = screen_size;
340 if (SCREEN_WIDTH > max_size.width)
342 new_size.width = static_cast<int>((float) new_size.width * float(max_size.width)/SCREEN_WIDTH);
343 SCREEN_WIDTH = static_cast<int>(max_size.width);
346 if (SCREEN_HEIGHT > max_size.height)
348 new_size.height = static_cast<int>((float) new_size.height * float(max_size.height)/SCREEN_HEIGHT);
349 SCREEN_HEIGHT = static_cast<int>(max_size.height);
352 // Clear the screen to avoid garbage in unreachable areas after we
353 // reset the coordinate system
354 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
355 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
356 SDL_RenderClear(renderer);
357 SDL_RenderPresent(renderer);
358 SDL_RenderClear(renderer);
360 viewport.x = std::max(0, (screen_size.width - new_size.width) / 2);
361 viewport.y = std::max(0, (screen_size.height - new_size.height) / 2);
362 viewport.w = std::min(new_size.width, screen_size.width);
363 viewport.h = std::min(new_size.height, screen_size.height);
365 SDL_RenderSetScale(renderer, 1.0f, 1.0f);
366 SDL_RenderSetViewport(renderer, &viewport);
367 SDL_RenderSetScale(renderer, g_config->magnification, g_config->magnification);
372 SDLRenderer::to_logical(int physical_x, int physical_y)
374 return Vector(static_cast<float>(physical_x - viewport.x) * SCREEN_WIDTH / viewport.w,
375 static_cast<float>(physical_y - viewport.y) * SCREEN_HEIGHT / viewport.h);
379 SDLRenderer::set_gamma(float gamma)
382 SDL_CalculateGammaRamp(gamma, ramp);
383 SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);