Moved some aspect ratio calculations into video/util.cpp
[supertux.git] / src / video / sdl / sdl_renderer.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //      Updated by GiBy 2013 for SDL2 <giby_the_kid@yahoo.fr>
4 //
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.
9 //
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.
14 //
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/>.
17
18 #include "video/sdl/sdl_renderer.hpp"
19
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"
25
26 #include <iomanip>
27 #include <iostream>
28 #include <physfs.h>
29 #include <sstream>
30 #include <stdexcept>
31 #include "SDL2/SDL_video.h"
32
33 #include "video/util.hpp"
34
35 SDLRenderer::SDLRenderer() :
36   window(),
37   renderer(),
38   viewport(),
39   desktop_size(0, 0)
40 {
41   Renderer::instance_ = this;
42
43   SDL_DisplayMode mode;
44   if (SDL_GetDesktopDisplayMode(0, &mode) != 0)
45   {
46     log_warning << "Couldn't get desktop display mode: " << SDL_GetError() << std::endl;
47   }
48   else
49   {
50     desktop_size = Size(mode.w, mode.h);
51   }
52
53   log_info << "creating SDLRenderer" << std::endl;
54   int width  = g_config->window_size.width;
55   int height = g_config->window_size.height;
56
57   int flags = SDL_WINDOW_RESIZABLE;
58   if(g_config->use_fullscreen)
59   {
60     flags |= SDL_WINDOW_FULLSCREEN;
61     width  = g_config->fullscreen_size.width;
62     height = g_config->fullscreen_size.height;
63   }
64
65   SCREEN_WIDTH = width;
66   SCREEN_HEIGHT = height;
67
68   viewport.x = 0;
69   viewport.y = 0;
70   viewport.w = width;
71   viewport.h = height;
72
73   SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");
74
75   int ret = SDL_CreateWindowAndRenderer(width, height, flags,
76                                         &window, &renderer);
77
78   if(ret != 0) {
79     std::stringstream msg;
80     msg << "Couldn't set video mode (" << width << "x" << height
81         << "): " << SDL_GetError();
82     throw std::runtime_error(msg.str());
83   }
84
85   SDL_RendererInfo info;
86   if (SDL_GetRendererInfo(renderer, &info) != 0)
87   {
88     log_warning << "Couldn't get RendererInfo: " << SDL_GetError() << std::endl;
89   }
90   else
91   {
92     log_info << "SDL_Renderer: " << info.name << std::endl;
93     log_info << "SDL_RendererFlags: " << std::endl;
94     if (info.flags & SDL_RENDERER_SOFTWARE) log_info << "  SDL_RENDERER_SOFTWARE" << std::endl;
95     if (info.flags & SDL_RENDERER_ACCELERATED) log_info << "  SDL_RENDERER_ACCELERATED" << std::endl;
96     if (info.flags & SDL_RENDERER_PRESENTVSYNC) log_info << "  SDL_RENDERER_PRESENTVSYNC" << std::endl;
97     if (info.flags & SDL_RENDERER_TARGETTEXTURE) log_info << "  SDL_RENDERER_TARGETTEXTURE" << std::endl;
98     log_info << "Texture Formats: " << std::endl;
99     for(size_t i = 0; i < info.num_texture_formats; ++i)
100     {
101       log_info << "  " << SDL_GetPixelFormatName(info.texture_formats[i]) << std::endl;
102     }
103     log_info << "Max Texture Width: " << info.max_texture_width << std::endl;
104     log_info << "Max Texture Height: " << info.max_texture_height << std::endl;
105   }
106
107   if(texture_manager == 0)
108     texture_manager = new TextureManager();
109
110   g_config->window_size = Size(width, height);
111   apply_config();
112 }
113
114 SDLRenderer::~SDLRenderer()
115 {
116   SDL_DestroyRenderer(renderer);
117   SDL_DestroyWindow(window);
118 }
119
120 void
121 SDLRenderer::draw_surface(const DrawingRequest& request)
122 {
123   SDLPainter::draw_surface(renderer, request);
124 }
125
126 void
127 SDLRenderer::draw_surface_part(const DrawingRequest& request)
128 {
129   SDLPainter::draw_surface_part(renderer, request);
130 }
131
132 void
133 SDLRenderer::draw_gradient(const DrawingRequest& request)
134 {
135   SDLPainter::draw_gradient(renderer, request);
136 }
137
138 void
139 SDLRenderer::draw_filled_rect(const DrawingRequest& request)
140 {
141   SDLPainter::draw_filled_rect(renderer, request);
142 }
143
144 void
145 SDLRenderer::draw_inverse_ellipse(const DrawingRequest& request)
146 {
147   SDLPainter::draw_inverse_ellipse(renderer, request);
148 }
149
150 void
151 SDLRenderer::do_take_screenshot()
152 {
153   // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
154   int width;
155   int height;
156   if (SDL_GetRendererOutputSize(renderer, &width, &height) != 0)
157   {
158     log_warning << "SDL_GetRenderOutputSize failed: " << SDL_GetError() << std::endl;
159   }
160   else
161   {
162 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
163     Uint32 rmask = 0xff000000;
164     Uint32 gmask = 0x00ff0000;
165     Uint32 bmask = 0x0000ff00;
166     Uint32 amask = 0x000000ff;
167 #else
168     Uint32 rmask = 0x000000ff;
169     Uint32 gmask = 0x0000ff00;
170     Uint32 bmask = 0x00ff0000;
171     Uint32 amask = 0xff000000;
172 #endif
173     SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32,
174                                                 rmask, gmask, bmask, amask);
175     if (!surface)
176     {
177       log_warning << "SDL_CreateRGBSurface failed: " << SDL_GetError() << std::endl;
178     }
179     else
180     {
181       int ret = SDL_RenderReadPixels(renderer, NULL,
182                                      SDL_PIXELFORMAT_ABGR8888,
183                                      surface->pixels,
184                                      surface->pitch);
185       if (ret != 0)
186       {
187         log_warning << "SDL_RenderReadPixels failed: " << SDL_GetError() << std::endl;
188       }
189       else
190       {
191         // save screenshot
192         static const std::string writeDir = PHYSFS_getWriteDir();
193         static const std::string dirSep = PHYSFS_getDirSeparator();
194         static const std::string baseName = "screenshot";
195         static const std::string fileExt = ".bmp";
196         std::string fullFilename;
197         for (int num = 0; num < 1000; num++) {
198           std::ostringstream oss;
199           oss << baseName;
200           oss << std::setw(3) << std::setfill('0') << num;
201           oss << fileExt;
202           std::string fileName = oss.str();
203           fullFilename = writeDir + dirSep + fileName;
204           if (!PHYSFS_exists(fileName.c_str())) {
205             SDL_SaveBMP(surface, fullFilename.c_str());
206             log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
207             return;
208           }
209         }
210         log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
211       }
212     }
213   }
214 }
215
216 void
217 SDLRenderer::flip()
218 {
219   SDL_RenderPresent(renderer);
220 }
221
222 void
223 SDLRenderer::resize(int w , int h)
224 {
225   g_config->window_size = Size(w, h);
226
227   apply_config();
228 }
229
230 void
231 SDLRenderer::apply_video_mode()
232 {
233   if (!g_config->use_fullscreen)
234   {
235     SDL_SetWindowFullscreen(window, 0);
236   }
237   else
238   {
239     SDL_DisplayMode mode;
240     mode.format = SDL_PIXELFORMAT_RGB888;
241     mode.w = g_config->fullscreen_size.width;
242     mode.h = g_config->fullscreen_size.height;
243     mode.refresh_rate = g_config->fullscreen_refresh_rate;
244     mode.driverdata = 0;
245
246     if (SDL_SetWindowDisplayMode(window, &mode) != 0)
247     {
248       log_warning << "failed to set display mode: "
249                   << mode.w << "x" << mode.h << "@" << mode.refresh_rate << ": "
250                   << SDL_GetError() << std::endl;
251     }
252     else
253     {
254       if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0)
255       {
256         log_warning << "failed to switch to fullscreen mode: "
257                     << mode.w << "x" << mode.h << "@" << mode.refresh_rate << ": "
258                     << SDL_GetError() << std::endl;
259       }
260       else
261       {
262         log_info << "switched to fullscreen mode: "
263                  << mode.w << "x" << mode.h << "@" << mode.refresh_rate << std::endl;
264       }
265     }
266   }
267
268 }
269
270 void
271 SDLRenderer::apply_viewport()
272 {
273   Size target_size = g_config->use_fullscreen ?
274     g_config->fullscreen_size :
275     g_config->window_size;
276
277   float pixel_aspect_ratio = 1.0f;
278   if (g_config->aspect_size != Size(0, 0))
279   {
280     pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
281                                                       g_config->aspect_size);
282   }
283   else if (g_config->use_fullscreen)
284   {
285     pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
286                                                       target_size);
287   }
288
289   // calculate the viewport
290   Size max_size(1280, 800);
291   Size min_size(640, 480);
292
293   Vector scale;
294   Size logical_size;
295   calculate_viewport(min_size, max_size,
296                      target_size,
297                      pixel_aspect_ratio,
298                      g_config->magnification,
299                      scale, logical_size, viewport);
300
301   SCREEN_WIDTH = logical_size.width;
302   SCREEN_HEIGHT = logical_size.height;
303
304   if (viewport.x != 0 || viewport.y != 0)
305   {
306     // Clear the screen to avoid garbage in unreachable areas after we
307     // reset the coordinate system
308     SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
309     SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
310     SDL_RenderClear(renderer);
311     SDL_RenderPresent(renderer);
312     SDL_RenderClear(renderer);
313   }
314
315   SDL_RenderSetScale(renderer, 1.0f, 1.0f);
316   SDL_RenderSetViewport(renderer, &viewport);
317   SDL_RenderSetScale(renderer, scale.x, scale.y);
318 }
319
320 void
321 SDLRenderer::apply_config()
322 {
323   apply_video_mode();
324   apply_viewport();
325 }
326
327 Vector
328 SDLRenderer::to_logical(int physical_x, int physical_y)
329 {
330   return Vector(static_cast<float>(physical_x - viewport.x) * SCREEN_WIDTH / viewport.w,
331                 static_cast<float>(physical_y - viewport.y) * SCREEN_HEIGHT / viewport.h);
332 }
333
334 void
335 SDLRenderer::set_gamma(float gamma)
336 {
337   Uint16 ramp[256];
338   SDL_CalculateGammaRamp(gamma, ramp);
339   SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
340 }
341
342 /* EOF */