From 09564e167a68cc67e0bc4b8126bcaa7c0258f3f2 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sat, 18 Apr 2020 01:46:31 +0200 Subject: [PATCH] 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. --- app/src/screen.c | 64 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/app/src/screen.c b/app/src/screen.c index bf11dc02..2a587270 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -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;