From: Ingo Ruhnke Date: Fri, 1 Aug 2014 19:01:22 +0000 (+0200) Subject: Reimplemented magnification and aspect ratio handling X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=4641bbe10a18c5bce64e16745bff8142c14fc060;p=supertux.git Reimplemented magnification and aspect ratio handling --- diff --git a/src/video/gl/gl_renderer.cpp b/src/video/gl/gl_renderer.cpp index 599eb5343..a1b97908c 100644 --- a/src/video/gl/gl_renderer.cpp +++ b/src/video/gl/gl_renderer.cpp @@ -27,6 +27,8 @@ #include "video/drawing_request.hpp" #include "video/gl/gl_surface_data.hpp" #include "video/gl/gl_texture.hpp" +#include "video/util.hpp" + #define LIGHTMAP_DIV 5 #ifdef GL_VERSION_ES_CM_1_0 @@ -508,83 +510,32 @@ GLRenderer::apply_config() apply_video_mode(screen_size, g_config->use_fullscreen); - if (target_aspect > 1.0f) - { - SCREEN_WIDTH = static_cast(screen_size.width * (target_aspect / desktop_aspect)); - SCREEN_HEIGHT = static_cast(screen_size.height); - } - else - { - SCREEN_WIDTH = static_cast(screen_size.width); - SCREEN_HEIGHT = static_cast(screen_size.height * (target_aspect / desktop_aspect)); - } - Size max_size(1280, 800); Size min_size(640, 480); - if (g_config->magnification == 0.0f) // Magic value that means 'minfill' - { - // This scales SCREEN_WIDTH/SCREEN_HEIGHT so that they never excede - // max_size.width/max_size.height resp. min_size.width/min_size.height - if (SCREEN_WIDTH > max_size.width || SCREEN_HEIGHT > max_size.height) - { - float scale1 = float(max_size.width)/SCREEN_WIDTH; - float scale2 = float(max_size.height)/SCREEN_HEIGHT; - float scale = (scale1 < scale2) ? scale1 : scale2; - SCREEN_WIDTH = static_cast(SCREEN_WIDTH * scale); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT * scale); - } - else if (SCREEN_WIDTH < min_size.width || SCREEN_HEIGHT < min_size.height) - { - float scale1 = float(min_size.width)/SCREEN_WIDTH; - float scale2 = float(min_size.height)/SCREEN_HEIGHT; - float scale = (scale1 < scale2) ? scale1 : scale2; - SCREEN_WIDTH = static_cast(SCREEN_WIDTH * scale); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT * scale); - } + Vector scale; + Size logical_size; + calculate_viewport(min_size, max_size, + screen_size, + target_aspect / desktop_aspect, g_config->magnification, + scale, + logical_size, + viewport); - viewport.x = 0; - viewport.y = 0; - viewport.w = screen_size.width; - viewport.h = screen_size.height; + SCREEN_WIDTH = logical_size.width; + SCREEN_HEIGHT = logical_size.height; - glViewport(viewport.x, viewport.y, viewport.w, viewport.h); - } - else + if (viewport.x != 0 || viewport.y != 0) { - SCREEN_WIDTH = static_cast(SCREEN_WIDTH / g_config->magnification); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT / g_config->magnification); - - // This works by adding black borders around the screen to limit - // SCREEN_WIDTH/SCREEN_HEIGHT to max_size.width/max_size.height - Size new_size = screen_size; - - if (SCREEN_WIDTH > max_size.width) - { - new_size.width = static_cast((float) new_size.width * float(max_size.width)/SCREEN_WIDTH); - SCREEN_WIDTH = static_cast(max_size.width); - } - - if (SCREEN_HEIGHT > max_size.height) - { - new_size.height = static_cast((float) new_size.height * float(max_size.height)/SCREEN_HEIGHT); - SCREEN_HEIGHT = static_cast(max_size.height); - } - // Clear both buffers so that we get a clean black border without junk glClear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapWindow(window); glClear(GL_COLOR_BUFFER_BIT); SDL_GL_SwapWindow(window); - - viewport.x = std::max(0, (screen_size.width - new_size.width) / 2); - viewport.y = std::max(0, (screen_size.height - new_size.height) / 2); - viewport.w = std::min(new_size.width, screen_size.width); - viewport.h = std::min(new_size.height, screen_size.height); - - glViewport(viewport.x, viewport.y, viewport.w, viewport.h); } + glViewport(viewport.x, viewport.y, viewport.w, viewport.h); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); diff --git a/src/video/sdl/sdl_renderer.cpp b/src/video/sdl/sdl_renderer.cpp index 736b299f2..41019e9d1 100644 --- a/src/video/sdl/sdl_renderer.cpp +++ b/src/video/sdl/sdl_renderer.cpp @@ -30,6 +30,8 @@ #include #include "SDL2/SDL_video.h" +#include "video/util.hpp" + SDLRenderer::SDLRenderer() : window(), renderer(), @@ -39,7 +41,7 @@ SDLRenderer::SDLRenderer() : Renderer::instance_ = this; SDL_DisplayMode mode; - SDL_GetCurrentDisplayMode(0, &mode); + SDL_GetDesktopDisplayMode(0, &mode); desktop_size = Size(mode.w, mode.h); log_info << "creating SDLRenderer" << std::endl; @@ -59,8 +61,8 @@ SDLRenderer::SDLRenderer() : viewport.x = 0; viewport.y = 0; - viewport.w = SCREEN_WIDTH; - viewport.h = SCREEN_HEIGHT; + viewport.w = width; + viewport.h = height; SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); @@ -99,6 +101,7 @@ SDLRenderer::SDLRenderer() : if(texture_manager == 0) texture_manager = new TextureManager(); + g_config->window_size = Size(width, height); apply_config(); } @@ -219,44 +222,8 @@ SDLRenderer::resize(int w , int h) } void -SDLRenderer::apply_config() +SDLRenderer::apply_video_mode() { - if (false) - { - log_info << "Applying Config:" - << "\n Desktop: " << desktop_size.width << "x" << desktop_size.height - << "\n Window: " << g_config->window_size - << "\n FullRes: " << g_config->fullscreen_size - << "\n Aspect: " << g_config->aspect_size - << "\n Magnif: " << g_config->magnification - << std::endl; - } - - float target_aspect = static_cast(desktop_size.width) / static_cast(desktop_size.height); - if (g_config->aspect_size != Size(0, 0)) - { - target_aspect = float(g_config->aspect_size.width) / float(g_config->aspect_size.height); - } - - float desktop_aspect = 4.0f / 3.0f; // random default fallback guess - if (desktop_size.width != -1 && desktop_size.height != -1) - { - desktop_aspect = float(desktop_size.width) / float(desktop_size.height); - } - - Size screen_size; - - // Get the screen width - if (g_config->use_fullscreen) - { - screen_size = g_config->fullscreen_size; - desktop_aspect = float(screen_size.width) / float(screen_size.height); - } - else - { - screen_size = g_config->window_size; - } - if (!g_config->use_fullscreen) { SDL_SetWindowFullscreen(window, 0); @@ -282,73 +249,57 @@ SDLRenderer::apply_config() } } - if (target_aspect > 1.0f) +} + +void +SDLRenderer::apply_viewport() +{ + // calculate the aspect ratio + float target_aspect = static_cast(desktop_size.width) / static_cast(desktop_size.height); + if (g_config->aspect_size != Size(0, 0)) { - SCREEN_WIDTH = static_cast(screen_size.width * (target_aspect / desktop_aspect)); - SCREEN_HEIGHT = static_cast(screen_size.height); + target_aspect = float(g_config->aspect_size.width) / float(g_config->aspect_size.height); } - else + + float desktop_aspect = 4.0f / 3.0f; // random default fallback guess + if (desktop_size.width != -1 && desktop_size.height != -1) { - SCREEN_WIDTH = static_cast(screen_size.width); - SCREEN_HEIGHT = static_cast(screen_size.height * (target_aspect / desktop_aspect)); + desktop_aspect = float(desktop_size.width) / float(desktop_size.height); } - Size max_size(1280, 800); - Size min_size(640, 480); + Size screen_size; - if (g_config->magnification == 0.0f) // Magic value that means 'minfill' + // Get the screen width + if (g_config->use_fullscreen) { - float magnification = 1.0f; - - // This scales SCREEN_WIDTH/SCREEN_HEIGHT so that they never excede - // max_size.width/max_size.height resp. min_size.width/min_size.height - if (SCREEN_WIDTH > max_size.width || SCREEN_HEIGHT > max_size.height) - { - float scale1 = float(max_size.width)/SCREEN_WIDTH; - float scale2 = float(max_size.height)/SCREEN_HEIGHT; - magnification = (scale1 < scale2) ? scale1 : scale2; - SCREEN_WIDTH = static_cast(SCREEN_WIDTH * magnification); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT * magnification); - } - else if (SCREEN_WIDTH < min_size.width || SCREEN_HEIGHT < min_size.height) - { - float scale1 = float(min_size.width)/SCREEN_WIDTH; - float scale2 = float(min_size.height)/SCREEN_HEIGHT; - magnification = (scale1 < scale2) ? scale1 : scale2; - SCREEN_WIDTH = static_cast(SCREEN_WIDTH * magnification); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT * magnification); - } - - viewport.x = 0; - viewport.y = 0; - viewport.w = screen_size.width; - viewport.h = screen_size.height; - - SDL_RenderSetScale(renderer, 1.0f, 1.0f); - SDL_RenderSetViewport(renderer, &viewport); - SDL_RenderSetScale(renderer, magnification, magnification); + screen_size = g_config->fullscreen_size; + desktop_aspect = float(screen_size.width) / float(screen_size.height); } else { - SCREEN_WIDTH = static_cast(SCREEN_WIDTH / g_config->magnification); - SCREEN_HEIGHT = static_cast(SCREEN_HEIGHT / g_config->magnification); + screen_size = g_config->window_size; + } - // This works by adding black borders around the screen to limit - // SCREEN_WIDTH/SCREEN_HEIGHT to max_size.width/max_size.height - Size new_size = screen_size; + // calculate the viewport + Size max_size(1280, 800); + Size min_size(640, 480); - if (SCREEN_WIDTH > max_size.width) - { - new_size.width = static_cast((float) new_size.width * float(max_size.width)/SCREEN_WIDTH); - SCREEN_WIDTH = static_cast(max_size.width); - } + // FIXME: don't do this, save window size + Size window_size; + SDL_GetWindowSize(window, &window_size.width, &window_size.height); - if (SCREEN_HEIGHT > max_size.height) - { - new_size.height = static_cast((float) new_size.height * float(max_size.height)/SCREEN_HEIGHT); - SCREEN_HEIGHT = static_cast(max_size.height); - } + Vector scale; + Size logical_size; + calculate_viewport(min_size, max_size, window_size, + target_aspect / desktop_aspect, + g_config->magnification, + scale, logical_size, viewport); + + SCREEN_WIDTH = logical_size.width; + SCREEN_HEIGHT = logical_size.height; + if (viewport.x != 0 || viewport.y != 0) + { // Clear the screen to avoid garbage in unreachable areas after we // reset the coordinate system SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); @@ -356,16 +307,18 @@ SDLRenderer::apply_config() SDL_RenderClear(renderer); SDL_RenderPresent(renderer); SDL_RenderClear(renderer); + } - viewport.x = std::max(0, (screen_size.width - new_size.width) / 2); - viewport.y = std::max(0, (screen_size.height - new_size.height) / 2); - viewport.w = std::min(new_size.width, screen_size.width); - viewport.h = std::min(new_size.height, screen_size.height); + SDL_RenderSetScale(renderer, 1.0f, 1.0f); + SDL_RenderSetViewport(renderer, &viewport); + SDL_RenderSetScale(renderer, scale.x, scale.y); +} - SDL_RenderSetScale(renderer, 1.0f, 1.0f); - SDL_RenderSetViewport(renderer, &viewport); - SDL_RenderSetScale(renderer, g_config->magnification, g_config->magnification); - } +void +SDLRenderer::apply_config() +{ + apply_video_mode(); + apply_viewport(); } Vector diff --git a/src/video/sdl/sdl_renderer.hpp b/src/video/sdl/sdl_renderer.hpp index ef8607eec..0206e84f9 100644 --- a/src/video/sdl/sdl_renderer.hpp +++ b/src/video/sdl/sdl_renderer.hpp @@ -38,10 +38,13 @@ public: Vector to_logical(int physical_x, int physical_y); void set_gamma(float gamma); SDL_Window* get_window() const { return window; } - SDL_Renderer* get_sdl_renderer() const { return renderer; }; private: + void apply_video_mode(); + void apply_viewport(); + +private: SDL_Window* window; SDL_Renderer* renderer; SDL_Rect viewport; diff --git a/src/video/util.cpp b/src/video/util.cpp new file mode 100644 index 000000000..5b10009c5 --- /dev/null +++ b/src/video/util.cpp @@ -0,0 +1,131 @@ +// SuperTux +// Copyright (C) 2014 Ingo Ruhnke +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "video/util.hpp" + +#include + +#include "math/size.hpp" +#include "math/vector.hpp" + +namespace { + +inline Size +apply_pixel_aspect_ratio_pre(const Size& window_size, float pixel_aspect_ratio) +{ + if (pixel_aspect_ratio < 1.0f) + { + return Size(window_size.width * pixel_aspect_ratio, + window_size.height); + } + else + { + return Size(window_size.width, + window_size.height * pixel_aspect_ratio); + } +} + +inline void +apply_pixel_aspect_ratio_post(const Size& real_window_size, const Size& window_size, float scale, + SDL_Rect& out_viewport, Vector& out_scale) +{ + Vector transform(static_cast(real_window_size.width) / window_size.width, + static_cast(real_window_size.height) / window_size.height); + out_viewport.x *= transform.x; + out_viewport.y *= transform.y; + + out_viewport.w *= transform.x; + out_viewport.h *= transform.y; + + out_scale.x = scale * transform.x; + out_scale.y = scale * transform.y; + +} + +inline float +calculate_scale(const Size& min_size, const Size& max_size, + const Size& window_size, + float magnification) +{ + float scale = magnification; + if (scale == 0.0f) // magic value + { + scale = 1.0f; + + // Find the minimum magnification that is needed to fill the screen + if (window_size.width > max_size.width || + window_size.height > max_size.height) + { + scale = std::max(static_cast(window_size.width) / max_size.width, + static_cast(window_size.height) / max_size.height); + } + + // If the resulting area would violate min_size, scale it down + if (window_size.width / scale < min_size.width || + window_size.height / scale < min_size.height) + { + scale = std::min(static_cast(window_size.width) / min_size.width, + static_cast(window_size.height) / min_size.height); + } + } + + return scale; +} + +inline SDL_Rect +calculate_viewport(const Size& max_size, const Size& window_size, float scale) +{ + SDL_Rect viewport; + + viewport.w = std::min(window_size.width, + static_cast(scale * max_size.width)); + viewport.h = std::min(window_size.height, + static_cast(scale * max_size.height)); + + // Center the viewport in the window + viewport.x = std::max(0, (window_size.width - viewport.w) / 2); + viewport.y = std::max(0, (window_size.height - viewport.h) / 2); + + return viewport; +} + +} // namespace + +void calculate_viewport(const Size& min_size, const Size& max_size, + const Size& real_window_size, + float pixel_aspect_ratio, float magnification, + Vector& out_scale, + Size& out_logical_size, + SDL_Rect& out_viewport) +{ + // Transform the real window_size by the aspect ratio, then do + // calculations on that virtual window_size + Size window_size = apply_pixel_aspect_ratio_pre(real_window_size, pixel_aspect_ratio); + + float scale = calculate_scale(min_size, max_size, window_size, magnification); + + // Calculate the new viewport size + out_viewport = calculate_viewport(max_size, window_size, scale); + + out_logical_size.width = static_cast(out_viewport.w / scale); + out_logical_size.height = static_cast(out_viewport.h / scale); + + // Transform the virtual window_size back into real window coordinates + apply_pixel_aspect_ratio_post(real_window_size, window_size, scale, + out_viewport, out_scale); +} + +/* EOF */ diff --git a/src/video/util.hpp b/src/video/util.hpp new file mode 100644 index 000000000..8073d9bfd --- /dev/null +++ b/src/video/util.hpp @@ -0,0 +1,34 @@ +// SuperTux +// Copyright (C) 2013 Ingo Ruhnke +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HEADER_SUPERTUX_VIDEO_UTIL_HPP +#define HEADER_SUPERTUX_VIDEO_UTIL_HPP + +#include "SDL_rect.h" + +class Size; +class Vector; + +void calculate_viewport(const Size& min_size, const Size& max_size, + const Size& real_window_size, + float pixel_aspect_ratio, float magnification, + Vector& out_scale, + Size& out_logical_size, + SDL_Rect& out_viewport); + +#endif + +/* EOF */