Stabilize auto-resize

The window dimensions are integers, so resizing to fit the content may
not be exact.

When computing the optimal size, it could cause to reduce the width and
height alternatively by few pixels, making the "optimal size" unstable.

For the same reason, during rendering, the content size could be
different from the optimal window size, leading to black borders
(supposed to be removed in optimal size).

To avoid this problem, always choose the same dimension if the choice to
resize the width or the height is in the error range.
logical_size.6
Romain Vimont 4 years ago
parent 20cde79980
commit 09564e167a

@ -87,6 +87,26 @@ get_preferred_display_bounds(struct size *bounds) {
return true;
}
// Indicate if the width should be kept on computing the optimal rectangle size
// to display the content in the window.
// Return true if the width should be kept, false if the height should be kept.
static bool
should_keep_width(struct size window_size, struct size content_size) {
// 32 bits because we need to multiply two 16 bits values
uint32_t ww = window_size.width;
uint32_t wh = window_size.height;
uint32_t cw = content_size.width;
uint32_t ch = content_size.height;
// To avoid keeping alternatively width and height on successive resizes
// due to rounding to integer, always prefer height (arbitrarily) if in the
// error range.
// err = ceil of content aspect ratio
unsigned err = (cw + ch - 1) / ch;
return cw * wh > ch * (ww + err);
}
// return the optimal size of the window, with the following constraints:
// - it attempts to keep at least one dimension of the current_size (i.e. it
// crops the black borders)
@ -99,33 +119,33 @@ get_optimal_size(struct size current_size, struct size content_size) {
return current_size;
}
struct size display_size;
// 32 bits because we need to multiply two 16 bits values
uint32_t w;
uint32_t h;
struct size window_size;
struct size display_size;
if (!get_preferred_display_bounds(&display_size)) {
// could not get display bounds, do not constraint the size
w = current_size.width;
h = current_size.height;
window_size.width = current_size.width;
window_size.height = current_size.height;
} else {
w = MIN(current_size.width, display_size.width);
h = MIN(current_size.height, display_size.height);
window_size.width = MIN(current_size.width, display_size.width);
window_size.height = MIN(current_size.height, display_size.height);
}
bool keep_width = content_size.width * h > content_size.height * w;
bool keep_width = should_keep_width(window_size, content_size);
if (keep_width) {
// remove black borders on top and bottom
h = content_size.height * w / content_size.width;
window_size.height = content_size.height * window_size.width
/ content_size.width;
} else {
// remove black borders on left and right (or none at all if it already
// fits)
w = content_size.width * h / content_size.height;
window_size.width = content_size.width * window_size.height
/ content_size.height;
}
// w and h must fit into 16 bits
assert(w < 0x10000 && h < 0x10000);
return (struct size) {w, h};
// width and height must fit into 16 bits
assert(window_size.width < 0x10000 && window_size.height < 0x10000);
return window_size;
}
// same as get_optimal_size(), but read the current size from the window
@ -164,17 +184,19 @@ get_initial_optimal_size(struct size content_size, uint16_t req_width,
static void
update_content_rect(struct screen *screen) {
int ww;
int wh;
SDL_GL_GetDrawableSize(screen->window, &ww, &wh);
int w;
int h;
SDL_GL_GetDrawableSize(screen->window, &w, &h);
// 32 bits because we need to multiply two 16 bits values
uint32_t cw = screen->content_size.width;
uint32_t ch = screen->content_size.height;
struct size window_size = {w, h};
uint16_t ww = window_size.width;
uint16_t wh = window_size.height;
uint16_t cw = screen->content_size.width;
uint16_t ch = screen->content_size.height;
SDL_Rect *rect = &screen->rect;
bool keep_width = cw * wh > ch * ww;
bool keep_width = should_keep_width(window_size, screen->content_size);
if (keep_width) {
rect->x = 0;
rect->w = ww;

Loading…
Cancel
Save