diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index ac5499d7095..9d1e19d146f 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1328,6 +1328,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,canvas_items,viewport"), "disabled");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/aspect", PROPERTY_HINT_ENUM, "ignore,keep,keep_width,keep_height,expand"), "keep");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::FLOAT, "display/window/stretch/scale", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), 1.0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "display/window/stretch/scale_mode", PROPERTY_HINT_ENUM, "fractional,integer"), "fractional");
GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"), 16384);
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 1a77e7c5255..1390d2e9ba2 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -712,6 +712,10 @@
+ The scale factor multiplier to use for 2D elements. This multiplies the final scale factor determined by [member display/window/stretch/mode]. If using the [b]Disabled[/b] stretch mode, this scale factor is applied as-is. This can be adjusted to make the UI easier to read on certain displays.
+
+
+ The policy to use to determine the final scale factor for 2D elements. This affects how [member display/window/stretch/scale] is applied, in addition to the automatic scale factor determined by [member display/window/stretch/mode].
If [code]true[/code] subwindows are embedded in the main window.
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index d194f7dd6c6..4d72f0845ec 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -505,6 +505,9 @@
Base size of the content (i.e. nodes that are drawn inside the window). If non-zero, [Window]'s content will be scaled when the window is resized to a different size.
+
+ The policy to use to determine the final scale factor for 2D elements. This affects how [member content_scale_factor] is applied, in addition to the automatic scale factor determined by [member content_scale_size].
+
The screen the window is currently on.
@@ -766,6 +769,12 @@
The content's aspect will be preserved. If the target size has different aspect from the base one, the content will stay in the top-left corner and add an extra visible area in the stretched space.
+
+ The content will be stretched according to a fractional factor. This fills all the space available in the window, but allows "pixel wobble" to occur due to uneven pixel scaling.
+
+
+ The content will be stretched only according to an integer factor, preserving sharp pixels. This may leave a black background visible on the window's edges depending on the window size.
+
Automatic layout direction, determined from the parent window layout direction.
diff --git a/main/main.cpp b/main/main.cpp
index fbd0b75e58a..2af63a3391b 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2835,6 +2835,7 @@ bool Main::start() {
Size2i stretch_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"),
GLOBAL_GET("display/window/size/viewport_height"));
real_t stretch_scale = GLOBAL_GET("display/window/stretch/scale");
+ String stretch_scale_mode = GLOBAL_GET("display/window/stretch/scale_mode");
Window::ContentScaleMode cs_sm = Window::CONTENT_SCALE_MODE_DISABLED;
if (stretch_mode == "canvas_items") {
@@ -2854,8 +2855,14 @@ bool Main::start() {
cs_aspect = Window::CONTENT_SCALE_ASPECT_EXPAND;
}
+ Window::ContentScaleStretch cs_stretch = Window::CONTENT_SCALE_STRETCH_FRACTIONAL;
+ if (stretch_scale_mode == "integer") {
+ cs_stretch = Window::CONTENT_SCALE_STRETCH_INTEGER;
+ }
+
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);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index dbc778deec2..a846f953861 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -929,6 +929,17 @@ void Window::_update_viewport_size() {
float font_oversampling = 1.0;
window_transform = Transform2D();
+ if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
+ // We always want to make sure that the content scale factor is a whole
+ // number, else there will be pixel wobble no matter what.
+ content_scale_factor = Math::floor(content_scale_factor);
+
+ // A content scale factor of zero is pretty useless.
+ if (content_scale_factor < 1) {
+ content_scale_factor = 1;
+ }
+ }
+
if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) {
font_oversampling = content_scale_factor;
final_size = size;
@@ -982,13 +993,26 @@ void Window::_update_viewport_size() {
screen_size = screen_size.floor();
viewport_size = viewport_size.floor();
+ if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
+ Size2i screen_scale = (screen_size / viewport_size).floor();
+ int scale_factor = MIN(screen_scale.x, screen_scale.y);
+
+ if (scale_factor < 1) {
+ scale_factor = 1;
+ }
+
+ screen_size = viewport_size * scale_factor;
+ }
+
Size2 margin;
Size2 offset;
- if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) {
+ if (screen_size.x < video_mode.x) {
margin.x = Math::round((video_mode.x - screen_size.x) / 2.0);
offset.x = Math::round(margin.x * viewport_size.y / screen_size.y);
- } else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) {
+ }
+
+ if (screen_size.y < video_mode.y) {
margin.y = Math::round((video_mode.y - screen_size.y) / 2.0);
offset.y = Math::round(margin.y * viewport_size.x / screen_size.x);
}
@@ -1236,6 +1260,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;
@@ -2350,6 +2383,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);
@@ -2460,7 +2496,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"), "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("Localization", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
@@ -2512,6 +2549,9 @@ 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(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 1695a227888..30a91829073 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -77,6 +77,11 @@ public:
CONTENT_SCALE_ASPECT_EXPAND,
};
+ enum ContentScaleStretch {
+ CONTENT_SCALE_STRETCH_FRACTIONAL,
+ CONTENT_SCALE_STRETCH_INTEGER,
+ };
+
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
@@ -130,6 +135,7 @@ private:
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();
@@ -289,6 +295,9 @@ public:
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;
@@ -399,6 +408,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);
VARIANT_ENUM_CAST(Window::WindowInitialPosition);