1// SuperTux
2// Copyright (C) 2016 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "viewport.hpp"
18
19#include <algorithm>
20
21#include "math/rect.hpp"
22#include "math/size.hpp"
23#include "math/vector.hpp"
24#include "supertux/gameconfig.hpp"
25#include "supertux/globals.hpp"
26
27// Minimum and maximum size of the virtual screen, note that the
28// maximum must not exceed X/Y_OFFSCREEN_DISTANCE or enemies end up
29// spawning on screen instead of off-screen.
30const Size Viewport::s_max_size(1368, 800);
31const Size Viewport::s_min_size(640, 480);
32
33namespace {
34
35inline Size
36apply_pixel_aspect_ratio_pre(const Size& window_size, float pixel_aspect_ratio)
37{
38 return Size(static_cast<int>(static_cast<float>(window_size.width) * pixel_aspect_ratio),
39 window_size.height);
40}
41
42inline void
43apply_pixel_aspect_ratio_post(const Size& real_window_size, const Size& window_size, float scale,
44 Rect& out_viewport, Vector& out_scale)
45{
46 Vector transform(static_cast<float>(real_window_size.width) / static_cast<float>(window_size.width),
47 static_cast<float>(real_window_size.height) / static_cast<float>(window_size.height));
48
49 out_viewport.left = static_cast<int>(static_cast<float>(out_viewport.left) * transform.x);
50 out_viewport.top = static_cast<int>(static_cast<float>(out_viewport.top) * transform.y);
51 out_viewport.right = static_cast<int>(static_cast<float>(out_viewport.right) * transform.x);
52 out_viewport.bottom = static_cast<int>(static_cast<float>(out_viewport.bottom) * transform.y);
53
54 out_scale.x = scale * transform.x;
55 out_scale.y = scale * transform.y;
56}
57
58inline float
59calculate_scale(const Size& min_size, const Size& max_size,
60 const Size& window_size,
61 float magnification)
62{
63 float scale = magnification;
64 if (scale == 0.0f) // magic value
65 {
66 scale = 1.0f;
67
68 // Find the minimum magnification that is needed to fill the screen
69 if (window_size.width > max_size.width ||
70 window_size.height > max_size.height)
71 {
72 scale = std::max(static_cast<float>(window_size.width) / static_cast<float>(max_size.width),
73 static_cast<float>(window_size.height) / static_cast<float>(max_size.height));
74 }
75
76 // If the resulting area would violate min_size, scale it down
77 if (static_cast<float>(window_size.width) / scale < static_cast<float>(min_size.width) ||
78 static_cast<float>(window_size.height) / scale < static_cast<float>(min_size.height))
79 {
80 scale = std::min(static_cast<float>(window_size.width) / static_cast<float>(min_size.width),
81 static_cast<float>(window_size.height) / static_cast<float>(min_size.height));
82 }
83 }
84
85 return scale;
86}
87
88inline Rect
89calculate_viewport(const Size& max_size, const Size& window_size, float scale)
90{
91 int viewport_width = std::min(window_size.width,
92 static_cast<int>(scale * static_cast<float>(max_size.width)));
93 int viewport_height = std::min(window_size.height,
94 static_cast<int>(scale * static_cast<float>(max_size.height)));
95
96 // Center the viewport in the window
97 Rect viewport;
98
99 viewport.left = std::max(0, (window_size.width - viewport_width) / 2);
100 viewport.top = std::max(0, (window_size.height - viewport_height) / 2);
101
102 viewport.right = viewport.left + viewport_width;
103 viewport.bottom = viewport.top + viewport_height;
104
105 return viewport;
106}
107
108void calculate_viewport(const Size& min_size, const Size& max_size,
109 const Size& real_window_size,
110 float pixel_aspect_ratio, float magnification,
111 Vector& out_scale,
112 Rect& out_viewport)
113{
114 // Transform the real window_size by the aspect ratio, then do
115 // calculations on that virtual window_size
116 Size window_size = apply_pixel_aspect_ratio_pre(real_window_size, pixel_aspect_ratio);
117
118 float scale = calculate_scale(min_size, max_size, window_size, magnification);
119
120 // Calculate the new viewport size
121 out_viewport = calculate_viewport(max_size, window_size, scale);
122
123 // Transform the virtual window_size back into real window coordinates
124 apply_pixel_aspect_ratio_post(real_window_size, window_size, scale,
125 out_viewport, out_scale);
126}
127
128float calculate_pixel_aspect_ratio(const Size& source, const Size& target)
129{
130 float source_aspect = 16.0f / 9.0f; // random guess
131 if (source != Size(0, 0))
132 {
133 source_aspect =
134 static_cast<float>(source.width) /
135 static_cast<float>(source.height);
136 }
137
138 float target_aspect =
139 static_cast<float>(target.width) /
140 static_cast<float>(target.height);
141
142 return target_aspect / source_aspect;
143}
144
145} // namespace
146
147Viewport
148Viewport::from_size(const Size& target_size, const Size& desktop_size)
149{
150 float pixel_aspect_ratio = 1.0f;
151 if (g_config->aspect_size != Size(0, 0))
152 {
153 pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
154 g_config->aspect_size);
155 }
156 else if (g_config->use_fullscreen)
157 {
158 pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
159 target_size);
160 }
161
162 // calculate the viewport
163 Rect viewport;
164 Vector scale;
165 calculate_viewport(s_min_size, s_max_size,
166 target_size,
167 pixel_aspect_ratio,
168 g_config->magnification,
169 scale, viewport);
170
171 return Viewport(viewport, scale);
172}
173
174Viewport::Viewport() :
175 m_rect(),
176 m_scale()
177{
178}
179
180Viewport::Viewport(const Rect& rect, const Vector& scale) :
181 m_rect(rect),
182 m_scale(scale)
183{
184}
185
186int
187Viewport::get_screen_width() const
188{
189 return static_cast<int>(static_cast<float>(m_rect.get_width()) / m_scale.x);
190}
191
192int
193Viewport::get_screen_height() const
194{
195 return static_cast<int>(static_cast<float>(m_rect.get_height()) / m_scale.y);
196}
197
198Size
199Viewport::get_screen_size() const
200{
201 return Size(get_screen_width(), get_screen_height());
202}
203
204Vector
205Viewport::to_logical(int physical_x, int physical_y) const
206{
207 return Vector(static_cast<float>(physical_x - m_rect.left) / m_scale.x,
208 static_cast<float>(physical_y - m_rect.top) / m_scale.y);
209}
210
211bool
212Viewport::needs_clear_screen() const
213{
214 return (m_rect.left != 0 || m_rect.top != 0);
215}
216
217/* EOF */
218