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