From 3490b9ba9862469fa261649c08e7cec52877cd17 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Fri, 4 Feb 2022 02:22:16 +0100 Subject: [PATCH] Rework content scale to provide more options, add integer scaling - Add content stretch modes Fractional (default), Integer and Hybrid. - All modes with with any stretch mode and aspect. - Fractional behaves as Godot currently does when scaling viewports. Pixel art will only look good when the window is scaled at multiples of the original size. - Integer enforces an integer scale for the final display (rounded down from the automatically computed fractional scale). Black bars may be displayed on all sides when using the `viewport` stretch mode. This provides crisp pixel art appearance. - Hybrid enforces an integer scale on the internal rendering resolution, but the viewport will be stretched down to the final display with linear filtering. This provides good pixel art appearance, though not as crisp as the Integer mode. - Tweak project setting for stretch scale to allow values as low as 0.5 (values below 1 are valid, and can be useful for UI testing purposes). TODO: - Implement hybrid filtering mode. --- main/main.cpp | 21 +++++++++++++++- scene/main/window.cpp | 57 ++++++++++++++++++++++++++++++++++++++++--- scene/main/window.h | 11 +++++++++ 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/main/main.cpp b/main/main.cpp index 2bb67f17f1e0..cfc099746984 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2500,6 +2500,7 @@ bool Main::start() { String stretch_mode = GLOBAL_DEF_BASIC("display/window/stretch/mode", "disabled"); String stretch_aspect = GLOBAL_DEF_BASIC("display/window/stretch/aspect", "keep"); + String stretch_stretch = GLOBAL_DEF_BASIC("display/window/stretch/stretch", "fractional"); Size2i stretch_size = Size2i(GLOBAL_DEF_BASIC("display/window/size/viewport_width", 0), GLOBAL_DEF_BASIC("display/window/size/viewport_height", 0)); real_t stretch_scale = GLOBAL_DEF_BASIC("display/window/stretch/scale", 1.0); @@ -2522,8 +2523,16 @@ bool Main::start() { cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND; } + Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL; + if (stretch_stretch == "integer") { + cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER; + } else if (stretch_stretch == "hybrid") { + cs_stretch = Window::CONTENT_SCALE_STRETCH_HYBRID; + } + sml->get_root()->set_content_scale_mode(cs_sm); sml->get_root()->set_content_scale_aspect(cs_aspect); + sml->get_root()->set_content_scale_stretch(cs_stretch); sml->get_root()->set_content_scale_size(stretch_size); sml->get_root()->set_content_scale_factor(stretch_scale); @@ -2564,18 +2573,28 @@ bool Main::start() { "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport")); + GLOBAL_DEF_BASIC("display/window/stretch/aspect", "keep"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/aspect", PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand")); + + GLOBAL_DEF_BASIC("display/window/stretch/stretch", "fractional"); + ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/stretch", + PropertyInfo(Variant::STRING, + "display/window/stretch/stretch", + PROPERTY_HINT_ENUM, + "fractional,integer,hybrid")); + GLOBAL_DEF_BASIC("display/window/stretch/scale", 1.0); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/scale", PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, - "1.0,8.0,0.1")); + "0.5,8.0,0.01")); + sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true)); sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true)); GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 73e8f537d9d1..efd2979a4a37 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -626,6 +626,16 @@ void Window::_update_viewport_size() { float font_oversampling = 1.0; if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + // Enforce integer scaling to ensure pixel-perfect appearance of pixel art. + content_scale_factor = Math::floor(content_scale_factor); + } else if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_HYBRID) { + // Scale up to the nearest greater integer value, then scale down with linear filtering. + // This provides good (but not pixel-perfect) appearance for pixel art, and allows + // displaying at any output resolution. + WARN_PRINT("Hybrid content scaling for disabled stretch mode is not implemented yet."); + } + font_oversampling = content_scale_factor; final_size = size; final_size_override = Size2(size) / content_scale_factor; @@ -702,18 +712,40 @@ void Window::_update_viewport_size() { //_update_font_oversampling(1.0); } break; case CONTENT_SCALE_MODE_CANVAS_ITEMS: { + attach_to_screen_rect = Rect2(margin, screen_size); final_size = screen_size; final_size_override = viewport_size / content_scale_factor; - attach_to_screen_rect = Rect2(margin, screen_size); - font_oversampling = (screen_size.x / viewport_size.x) * content_scale_factor; + font_oversampling = (screen_size.x / viewport_size.x) * content_scale_factor; Size2 scale = Vector2(screen_size) / Vector2(final_size_override); + + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + // Enforce integer scaling to ensure pixel-perfect appearance of pixel art. + font_oversampling = Math::floor(font_oversampling); + scale = scale.floor(); + } else if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_HYBRID) { + // Scale up to the nearest greater integer value, then scale down with linear filtering. + // This provides good (but not pixel-perfect) appearance for pixel art, and allows + // displaying at any output resolution. + WARN_PRINT("Hybrid content scaling for canvas items stretch mode is not implemented yet."); + } + stretch_transform.scale(scale); } break; case CONTENT_SCALE_MODE_VIEWPORT: { - final_size = (viewport_size / content_scale_factor).floor(); attach_to_screen_rect = Rect2(margin, screen_size); + final_size = (viewport_size / content_scale_factor).floor(); + + if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) { + // Enforce integer scaling to ensure pixel-perfect appearance of pixel art. + WARN_PRINT("Integer content scaling for viewport stretch mode is not implemented yet."); + } else if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_HYBRID) { + // Scale up to the nearest greater integer value, then scale down with linear filtering. + // This provides good (but not pixel-perfect) appearance for pixel art, and allows + // displaying at any output resolution. + WARN_PRINT("Hybrid content scaling for viewport stretch mode is not implemented yet."); + } } break; } @@ -920,6 +952,15 @@ Window::ContentScaleAspect Window::get_content_scale_aspect() const { return content_scale_aspect; } +void Window::set_content_scale_stretch(ContentScaleStretch p_stretch) { + content_scale_stretch = p_stretch; + _update_viewport_size(); +} + +Window::ContentScaleStretch Window::get_content_scale_stretch() const { + return content_scale_stretch; +} + void Window::set_content_scale_factor(real_t p_factor) { ERR_FAIL_COND(p_factor <= 0); content_scale_factor = p_factor; @@ -1588,6 +1629,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect); ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect); + ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch); + ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch); + ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor); ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor); @@ -1663,7 +1707,8 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor"), "set_content_scale_factor", "get_content_scale_factor"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer,Hybrid"), "set_content_scale_stretch", "get_content_scale_stretch"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor"); ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); @@ -1710,6 +1755,10 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT); BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_FRACTIONAL); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER); + BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_HYBRID); + BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE); BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR); diff --git a/scene/main/window.h b/scene/main/window.h index c060f1d79dde..71f4ccbd5105 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -73,6 +73,12 @@ class Window : public Viewport { CONTENT_SCALE_ASPECT_EXPAND, }; + enum ContentScaleStretch { + CONTENT_SCALE_STRETCH_FRACTIONAL, + CONTENT_SCALE_STRETCH_INTEGER, + CONTENT_SCALE_STRETCH_HYBRID, + }; + enum LayoutDirection { LAYOUT_DIRECTION_INHERITED, LAYOUT_DIRECTION_LOCALE, @@ -114,6 +120,7 @@ class Window : public Viewport { Size2i content_scale_size; ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED; ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE; + ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL; real_t content_scale_factor = 1.0; void _make_window(); @@ -233,6 +240,9 @@ class Window : public Viewport { void set_content_scale_aspect(ContentScaleAspect p_aspect); ContentScaleAspect get_content_scale_aspect() const; + void set_content_scale_stretch(ContentScaleStretch p_stretch); + ContentScaleStretch get_content_scale_stretch() const; + void set_content_scale_factor(real_t p_factor); real_t get_content_scale_factor() const; @@ -304,6 +314,7 @@ VARIANT_ENUM_CAST(Window::Mode); VARIANT_ENUM_CAST(Window::Flags); VARIANT_ENUM_CAST(Window::ContentScaleMode); VARIANT_ENUM_CAST(Window::ContentScaleAspect); +VARIANT_ENUM_CAST(Window::ContentScaleStretch); VARIANT_ENUM_CAST(Window::LayoutDirection); #endif // WINDOW_H