Browse Source

Merge pull request #80605 from bruvzg/bmp_font_scale

[Bitmap fonts] Add support for scaling.
Rémi Verschelde 1 year ago
parent
commit
b1fe1f1440

+ 3 - 0
doc/classes/FontFile.xml

@@ -588,6 +588,9 @@
 		<member name="fixed_size" type="int" setter="set_fixed_size" getter="get_fixed_size" default="0">
 		<member name="fixed_size" type="int" setter="set_fixed_size" getter="get_fixed_size" default="0">
 			Font size, used only for the bitmap fonts.
 			Font size, used only for the bitmap fonts.
 		</member>
 		</member>
+		<member name="fixed_size_scale_mode" type="int" setter="set_fixed_size_scale_mode" getter="get_fixed_size_scale_mode" enum="TextServer.FixedSizeScaleMode" default="0">
+			Scaling mode, used only for the bitmap fonts with [member fixed_size] greater than zero.
+		</member>
 		<member name="font_name" type="String" setter="set_font_name" getter="get_font_name" default="&quot;&quot;">
 		<member name="font_name" type="String" setter="set_font_name" getter="get_font_name" default="&quot;&quot;">
 			Font family name.
 			Font family name.
 		</member>
 		</member>

+ 3 - 0
doc/classes/ResourceImporterBMFont.xml

@@ -18,5 +18,8 @@
 		<member name="fallbacks" type="Array" setter="" getter="" default="[]">
 		<member name="fallbacks" type="Array" setter="" getter="" default="[]">
 			List of font fallbacks to use if a glyph isn't found in this bitmap font. Fonts at the beginning of the array are attempted first.
 			List of font fallbacks to use if a glyph isn't found in this bitmap font. Fonts at the beginning of the array are attempted first.
 		</member>
 		</member>
+		<member name="scaling_mode" type="int" setter="" getter="" default="2">
+			Font scaling mode.
+		</member>
 	</members>
 	</members>
 </class>
 </class>

+ 3 - 0
doc/classes/ResourceImporterImageFont.xml

@@ -34,5 +34,8 @@
 		<member name="rows" type="int" setter="" getter="" default="1">
 		<member name="rows" type="int" setter="" getter="" default="1">
 			Number of rows in the font image. See also [member columns].
 			Number of rows in the font image. See also [member columns].
 		</member>
 		</member>
+		<member name="scaling_mode" type="int" setter="" getter="" default="2">
+			Font scaling mode.
+		</member>
 	</members>
 	</members>
 </class>
 </class>

+ 24 - 0
doc/classes/TextServer.xml

@@ -165,6 +165,13 @@
 				Returns bitmap font fixed size.
 				Returns bitmap font fixed size.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="font_get_fixed_size_scale_mode" qualifiers="const">
+			<return type="int" enum="TextServer.FixedSizeScaleMode" />
+			<param index="0" name="font_rid" type="RID" />
+			<description>
+				Returned bitmap font scaling mode.
+			</description>
+		</method>
 		<method name="font_get_generate_mipmaps" qualifiers="const">
 		<method name="font_get_generate_mipmaps" qualifiers="const">
 			<return type="bool" />
 			<return type="bool" />
 			<param index="0" name="font_rid" type="RID" />
 			<param index="0" name="font_rid" type="RID" />
@@ -674,6 +681,14 @@
 				Sets bitmap font fixed size. If set to value greater than zero, same cache entry will be used for all font sizes.
 				Sets bitmap font fixed size. If set to value greater than zero, same cache entry will be used for all font sizes.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="font_set_fixed_size_scale_mode">
+			<return type="void" />
+			<param index="0" name="font_rid" type="RID" />
+			<param index="1" name="fixed_size_scale_mode" type="int" enum="TextServer.FixedSizeScaleMode" />
+			<description>
+				Sets bitmap font scaling mode. This property is used only if [code]fixed_size[/code] is greater than zero.
+			</description>
+		</method>
 		<method name="font_set_force_autohinter">
 		<method name="font_set_force_autohinter">
 			<return type="void" />
 			<return type="void" />
 			<param index="0" name="font_rid" type="RID" />
 			<param index="0" name="font_rid" type="RID" />
@@ -2006,5 +2021,14 @@
 		<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
 		<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
 			User defined structured text BiDi override function.
 			User defined structured text BiDi override function.
 		</constant>
 		</constant>
+		<constant name="FIXED_SIZE_SCALE_DISABLE" value="0" enum="FixedSizeScaleMode">
+			Bitmap font is not scaled.
+		</constant>
+		<constant name="FIXED_SIZE_SCALE_INTEGER_ONLY" value="1" enum="FixedSizeScaleMode">
+			Bitmap font is scaled to the closest integer multiple of the font's fixed size. This is the recommended option for pixel art fonts.
+		</constant>
+		<constant name="FIXED_SIZE_SCALE_ENABLED" value="2" enum="FixedSizeScaleMode">
+			Bitmap font is scaled to an arbitrary (fractional) size. This is the recommended option for non-pixel art fonts.
+		</constant>
 	</constants>
 	</constants>
 </class>
 </class>

+ 13 - 0
doc/classes/TextServerExtension.xml

@@ -144,6 +144,12 @@
 			<description>
 			<description>
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_font_get_fixed_size_scale_mode" qualifiers="virtual const">
+			<return type="int" enum="TextServer.FixedSizeScaleMode" />
+			<param index="0" name="font_rid" type="RID" />
+			<description>
+			</description>
+		</method>
 		<method name="_font_get_generate_mipmaps" qualifiers="virtual const">
 		<method name="_font_get_generate_mipmaps" qualifiers="virtual const">
 			<return type="bool" />
 			<return type="bool" />
 			<param index="0" name="font_rid" type="RID" />
 			<param index="0" name="font_rid" type="RID" />
@@ -590,6 +596,13 @@
 			<description>
 			<description>
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_font_set_fixed_size_scale_mode" qualifiers="virtual">
+			<return type="void" />
+			<param index="0" name="font_rid" type="RID" />
+			<param index="1" name="fixed_size_scale_mode" type="int" enum="TextServer.FixedSizeScaleMode" />
+			<description>
+			</description>
+		</method>
 		<method name="_font_set_force_autohinter" qualifiers="virtual">
 		<method name="_font_set_force_autohinter" qualifiers="virtual">
 			<return type="void" />
 			<return type="void" />
 			<param index="0" name="font_rid" type="RID" />
 			<param index="0" name="font_rid" type="RID" />

+ 3 - 0
editor/import/resource_importer_bmfont.cpp

@@ -63,12 +63,14 @@ void ResourceImporterBMFont::get_import_options(const String &p_path, List<Impor
 	r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
 
 
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
 }
 }
 
 
 Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
 	print_verbose("Importing BMFont font from: " + p_source_file);
 	print_verbose("Importing BMFont font from: " + p_source_file);
 
 
 	Array fallbacks = p_options["fallbacks"];
 	Array fallbacks = p_options["fallbacks"];
+	TextServer::FixedSizeScaleMode smode = (TextServer::FixedSizeScaleMode)p_options["scaling_mode"].operator int();
 
 
 	Ref<FontFile> font;
 	Ref<FontFile> font;
 	font.instantiate();
 	font.instantiate();
@@ -78,6 +80,7 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
 
 
 	font->set_allow_system_fallback(false);
 	font->set_allow_system_fallback(false);
 	font->set_fallbacks(fallbacks);
 	font->set_fallbacks(fallbacks);
+	font->set_fixed_size_scale_mode(smode);
 
 
 	int flg = 0;
 	int flg = 0;
 	if ((bool)p_options["compress"]) {
 	if ((bool)p_options["compress"]) {

+ 3 - 0
editor/import/resource_importer_imagefont.cpp

@@ -69,6 +69,7 @@ void ResourceImporterImageFont::get_import_options(const String &p_path, List<Im
 	r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), Array()));
 
 
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
 }
 }
 
 
 bool ResourceImporterImageFont::_decode_range(const String &p_token, int32_t &r_pos) {
 bool ResourceImporterImageFont::_decode_range(const String &p_token, int32_t &r_pos) {
@@ -98,6 +99,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
 	Array fallbacks = p_options["fallbacks"];
 	Array fallbacks = p_options["fallbacks"];
 	Rect2i img_margin = p_options["image_margin"];
 	Rect2i img_margin = p_options["image_margin"];
 	Rect2i char_margin = p_options["character_margin"];
 	Rect2i char_margin = p_options["character_margin"];
+	TextServer::FixedSizeScaleMode smode = (TextServer::FixedSizeScaleMode)p_options["scaling_mode"].operator int();
 
 
 	Ref<Image> img;
 	Ref<Image> img;
 	img.instantiate();
 	img.instantiate();
@@ -126,6 +128,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
 	font->set_oversampling(1.0f);
 	font->set_oversampling(1.0f);
 	font->set_fallbacks(fallbacks);
 	font->set_fallbacks(fallbacks);
 	font->set_texture_image(0, Vector2i(chr_height, 0), 0, img);
 	font->set_texture_image(0, Vector2i(chr_height, 0), 0, img);
+	font->set_fixed_size_scale_mode(smode);
 
 
 	int pos = 0;
 	int pos = 0;
 	for (int i = 0; i < ranges.size(); i++) {
 	for (int i = 0; i < ranges.size(); i++) {

+ 110 - 4
modules/text_server_adv/text_server_adv.cpp

@@ -2249,7 +2249,7 @@ void TextServerAdvanced::_font_set_msdf_size(const RID &p_font_rid, int64_t p_ms
 
 
 int64_t TextServerAdvanced::_font_get_msdf_size(const RID &p_font_rid) const {
 int64_t TextServerAdvanced::_font_get_msdf_size(const RID &p_font_rid) const {
 	FontAdvanced *fd = _get_font_data(p_font_rid);
 	FontAdvanced *fd = _get_font_data(p_font_rid);
-	ERR_FAIL_NULL_V(fd, false);
+	ERR_FAIL_NULL_V(fd, 0);
 
 
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	return fd->msdf_source_size;
 	return fd->msdf_source_size;
@@ -2265,12 +2265,28 @@ void TextServerAdvanced::_font_set_fixed_size(const RID &p_font_rid, int64_t p_f
 
 
 int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const {
 int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const {
 	FontAdvanced *fd = _get_font_data(p_font_rid);
 	FontAdvanced *fd = _get_font_data(p_font_rid);
-	ERR_FAIL_NULL_V(fd, false);
+	ERR_FAIL_NULL_V(fd, 0);
 
 
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	return fd->fixed_size;
 	return fd->fixed_size;
 }
 }
 
 
+void TextServerAdvanced::_font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
+	FontAdvanced *fd = _get_font_data(p_font_rid);
+	ERR_FAIL_NULL(fd);
+
+	MutexLock lock(fd->mutex);
+	fd->fixed_size_scale_mode = p_fixed_size_scale_mode;
+}
+
+TextServer::FixedSizeScaleMode TextServerAdvanced::_font_get_fixed_size_scale_mode(const RID &p_font_rid) const {
+	FontAdvanced *fd = _get_font_data(p_font_rid);
+	ERR_FAIL_NULL_V(fd, FIXED_SIZE_SCALE_DISABLE);
+
+	MutexLock lock(fd->mutex);
+	return fd->fixed_size_scale_mode;
+}
+
 void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 	FontAdvanced *fd = _get_font_data(p_font_rid);
 	FontAdvanced *fd = _get_font_data(p_font_rid);
 	ERR_FAIL_NULL(fd);
 	ERR_FAIL_NULL(fd);
@@ -2507,6 +2523,12 @@ double TextServerAdvanced::_font_get_ascent(const RID &p_font_rid, int64_t p_siz
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->ascent * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->ascent * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->ascent;
 		return fd->cache[size]->ascent;
 	}
 	}
@@ -2533,6 +2555,12 @@ double TextServerAdvanced::_font_get_descent(const RID &p_font_rid, int64_t p_si
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->descent * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->descent * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->descent;
 		return fd->cache[size]->descent;
 	}
 	}
@@ -2560,6 +2588,12 @@ double TextServerAdvanced::_font_get_underline_position(const RID &p_font_rid, i
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->underline_position * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->underline_position * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->underline_position;
 		return fd->cache[size]->underline_position;
 	}
 	}
@@ -2587,6 +2621,12 @@ double TextServerAdvanced::_font_get_underline_thickness(const RID &p_font_rid,
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->underline_thickness;
 		return fd->cache[size]->underline_thickness;
 	}
 	}
@@ -2619,6 +2659,12 @@ double TextServerAdvanced::_font_get_scale(const RID &p_font_rid, int64_t p_size
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->scale * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->scale * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->scale / fd->cache[size]->oversampling;
 		return fd->cache[size]->scale / fd->cache[size]->oversampling;
 	}
 	}
@@ -2828,6 +2874,12 @@ Vector2 TextServerAdvanced::_font_get_glyph_advance(const RID &p_font_rid, int64
 	double scale = _font_get_scale(p_font_rid, p_size);
 	double scale = _font_get_scale(p_font_rid, p_size);
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
 		return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return (gl[p_glyph | mod].advance + ea) * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
 	} else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
 		return (gl[p_glyph | mod].advance + ea).round();
 		return (gl[p_glyph | mod].advance + ea).round();
 	} else {
 	} else {
@@ -2875,6 +2927,12 @@ Vector2 TextServerAdvanced::_font_get_glyph_offset(const RID &p_font_rid, const
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
 		return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->fixed_size;
+		} else {
+			return gl[p_glyph | mod].rect.position * Math::round((double)p_size.x / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return gl[p_glyph | mod].rect.position;
 		return gl[p_glyph | mod].rect.position;
 	}
 	}
@@ -2920,6 +2978,12 @@ Vector2 TextServerAdvanced::_font_get_glyph_size(const RID &p_font_rid, const Ve
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
 		return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->fixed_size;
+		} else {
+			return gl[p_glyph | mod].rect.size * Math::round((double)p_size.x / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return gl[p_glyph | mod].rect.size;
 		return gl[p_glyph | mod].rect.size;
 	}
 	}
@@ -3143,6 +3207,12 @@ Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, i
 	double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
 	double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
 	if (fd->msdf) {
 	if (fd->msdf) {
 		scale = scale * (double)p_size / (double)fd->msdf_source_size;
 		scale = scale * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			scale = scale * (double)p_size / (double)fd->fixed_size;
+		} else {
+			scale = scale * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	}
 	}
 	for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
 	for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
 		points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
 		points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
@@ -3225,6 +3295,12 @@ Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_s
 	if (kern.has(p_glyph_pair)) {
 	if (kern.has(p_glyph_pair)) {
 		if (fd->msdf) {
 		if (fd->msdf) {
 			return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;
 			return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;
+		} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+			if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+				return kern[p_glyph_pair] * (double)p_size / (double)fd->fixed_size;
+			} else {
+				return kern[p_glyph_pair] * Math::round((double)p_size / (double)fd->fixed_size);
+			}
 		} else {
 		} else {
 			return kern[p_glyph_pair];
 			return kern[p_glyph_pair];
 		}
 		}
@@ -3235,6 +3311,12 @@ Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_s
 			FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
 			FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
 			if (fd->msdf) {
 			if (fd->msdf) {
 				return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;
 				return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;
+			} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+				if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+					return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->fixed_size;
+				} else {
+					return Vector2(delta.x, delta.y) * Math::round((double)p_size / (double)fd->fixed_size);
+				}
 			} else {
 			} else {
 				return Vector2(delta.x, delta.y);
 				return Vector2(delta.x, delta.y);
 			}
 			}
@@ -3499,8 +3581,20 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 						cpos.y = Math::floor(cpos.y);
 						cpos.y = Math::floor(cpos.y);
 						cpos.x = Math::floor(cpos.x);
 						cpos.x = Math::floor(cpos.x);
 					}
 					}
-					cpos += gl.rect.position;
+					Vector2 gpos = gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
+					if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+						if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+							double gl_scale = (double)p_size / (double)fd->fixed_size;
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						} else {
+							double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						}
+					}
+					cpos += gpos;
 					if (lcd_aa) {
 					if (lcd_aa) {
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 					} else {
 					} else {
@@ -3591,8 +3685,20 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R
 						cpos.y = Math::floor(cpos.y);
 						cpos.y = Math::floor(cpos.y);
 						cpos.x = Math::floor(cpos.x);
 						cpos.x = Math::floor(cpos.x);
 					}
 					}
-					cpos += gl.rect.position;
+					Vector2 gpos = gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
+					if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+						if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+							double gl_scale = (double)p_size / (double)fd->fixed_size;
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						} else {
+							double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						}
+					}
+					cpos += gpos;
 					if (lcd_aa) {
 					if (lcd_aa) {
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 					} else {
 					} else {

+ 4 - 0
modules/text_server_adv/text_server_adv.h

@@ -305,6 +305,7 @@ class TextServerAdvanced : public TextServerExtension {
 		bool mipmaps = false;
 		bool mipmaps = false;
 		bool msdf = false;
 		bool msdf = false;
 		int msdf_range = 14;
 		int msdf_range = 14;
+		FixedSizeScaleMode fixed_size_scale_mode = FIXED_SIZE_SCALE_DISABLE;
 		int msdf_source_size = 48;
 		int msdf_source_size = 48;
 		int fixed_size = 0;
 		int fixed_size = 0;
 		bool allow_system_fallback = true;
 		bool allow_system_fallback = true;
@@ -763,6 +764,9 @@ public:
 	MODBIND2(font_set_fixed_size, const RID &, int64_t);
 	MODBIND2(font_set_fixed_size, const RID &, int64_t);
 	MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
 	MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
 
 
+	MODBIND2(font_set_fixed_size_scale_mode, const RID &, FixedSizeScaleMode);
+	MODBIND1RC(FixedSizeScaleMode, font_get_fixed_size_scale_mode, const RID &);
+
 	MODBIND2(font_set_allow_system_fallback, const RID &, bool);
 	MODBIND2(font_set_allow_system_fallback, const RID &, bool);
 	MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
 	MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
 
 

+ 110 - 4
modules/text_server_fb/text_server_fb.cpp

@@ -1243,7 +1243,7 @@ void TextServerFallback::_font_set_msdf_size(const RID &p_font_rid, int64_t p_ms
 
 
 int64_t TextServerFallback::_font_get_msdf_size(const RID &p_font_rid) const {
 int64_t TextServerFallback::_font_get_msdf_size(const RID &p_font_rid) const {
 	FontFallback *fd = _get_font_data(p_font_rid);
 	FontFallback *fd = _get_font_data(p_font_rid);
-	ERR_FAIL_NULL_V(fd, false);
+	ERR_FAIL_NULL_V(fd, 0);
 
 
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	return fd->msdf_source_size;
 	return fd->msdf_source_size;
@@ -1259,12 +1259,28 @@ void TextServerFallback::_font_set_fixed_size(const RID &p_font_rid, int64_t p_f
 
 
 int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {
 int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {
 	FontFallback *fd = _get_font_data(p_font_rid);
 	FontFallback *fd = _get_font_data(p_font_rid);
-	ERR_FAIL_NULL_V(fd, false);
+	ERR_FAIL_NULL_V(fd, 0);
 
 
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	return fd->fixed_size;
 	return fd->fixed_size;
 }
 }
 
 
+void TextServerFallback::_font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
+	FontFallback *fd = _get_font_data(p_font_rid);
+	ERR_FAIL_NULL(fd);
+
+	MutexLock lock(fd->mutex);
+	fd->fixed_size_scale_mode = p_fixed_size_scale_mode;
+}
+
+TextServer::FixedSizeScaleMode TextServerFallback::_font_get_fixed_size_scale_mode(const RID &p_font_rid) const {
+	FontFallback *fd = _get_font_data(p_font_rid);
+	ERR_FAIL_NULL_V(fd, FIXED_SIZE_SCALE_DISABLE);
+
+	MutexLock lock(fd->mutex);
+	return fd->fixed_size_scale_mode;
+}
+
 void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 	FontFallback *fd = _get_font_data(p_font_rid);
 	FontFallback *fd = _get_font_data(p_font_rid);
 	ERR_FAIL_NULL(fd);
 	ERR_FAIL_NULL(fd);
@@ -1502,6 +1518,12 @@ double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_siz
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->ascent * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->ascent * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->ascent * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->ascent;
 		return fd->cache[size]->ascent;
 	}
 	}
@@ -1528,6 +1550,12 @@ double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_si
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->descent * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->descent * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->descent * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->descent;
 		return fd->cache[size]->descent;
 	}
 	}
@@ -1555,6 +1583,12 @@ double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, i
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->underline_position * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->underline_position * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->underline_position * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->underline_position;
 		return fd->cache[size]->underline_position;
 	}
 	}
@@ -1582,6 +1616,12 @@ double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid,
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->underline_thickness * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->underline_thickness * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->underline_thickness;
 		return fd->cache[size]->underline_thickness;
 	}
 	}
@@ -1614,6 +1654,12 @@ double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size;
 		return fd->cache[size]->scale * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return fd->cache[size]->scale * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return fd->cache[size]->scale * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return fd->cache[size]->scale / fd->cache[size]->oversampling;
 		return fd->cache[size]->scale / fd->cache[size]->oversampling;
 	}
 	}
@@ -1809,6 +1855,12 @@ Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64
 	double scale = _font_get_scale(p_font_rid, p_size);
 	double scale = _font_get_scale(p_font_rid, p_size);
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
 		return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->fixed_size;
+		} else {
+			return (gl[p_glyph | mod].advance + ea) * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	} else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
 	} else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
 		return (gl[p_glyph | mod].advance + ea).round();
 		return (gl[p_glyph | mod].advance + ea).round();
 	} else {
 	} else {
@@ -1856,6 +1908,12 @@ Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
 		return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->fixed_size;
+		} else {
+			return gl[p_glyph | mod].rect.position * Math::round((double)p_size.x / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return gl[p_glyph | mod].rect.position;
 		return gl[p_glyph | mod].rect.position;
 	}
 	}
@@ -1901,6 +1959,12 @@ Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Ve
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
 		return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size.x) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->fixed_size;
+		} else {
+			return gl[p_glyph | mod].rect.size * Math::round((double)p_size.x / (double)fd->fixed_size);
+		}
 	} else {
 	} else {
 		return gl[p_glyph | mod].rect.size;
 		return gl[p_glyph | mod].rect.size;
 	}
 	}
@@ -2124,6 +2188,12 @@ Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, i
 	double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
 	double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
 	if (fd->msdf) {
 	if (fd->msdf) {
 		scale = scale * (double)p_size / (double)fd->msdf_source_size;
 		scale = scale * (double)p_size / (double)fd->msdf_source_size;
+	} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+		if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+			scale = scale * (double)p_size / (double)fd->fixed_size;
+		} else {
+			scale = scale * Math::round((double)p_size / (double)fd->fixed_size);
+		}
 	}
 	}
 	for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
 	for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
 		points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
 		points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
@@ -2206,6 +2276,12 @@ Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_s
 	if (kern.has(p_glyph_pair)) {
 	if (kern.has(p_glyph_pair)) {
 		if (fd->msdf) {
 		if (fd->msdf) {
 			return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;
 			return kern[p_glyph_pair] * (double)p_size / (double)fd->msdf_source_size;
+		} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+			if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+				return kern[p_glyph_pair] * (double)p_size / (double)fd->fixed_size;
+			} else {
+				return kern[p_glyph_pair] * Math::round((double)p_size / (double)fd->fixed_size);
+			}
 		} else {
 		} else {
 			return kern[p_glyph_pair];
 			return kern[p_glyph_pair];
 		}
 		}
@@ -2218,6 +2294,12 @@ Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_s
 			FT_Get_Kerning(fd->cache[size]->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta);
 			FT_Get_Kerning(fd->cache[size]->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta);
 			if (fd->msdf) {
 			if (fd->msdf) {
 				return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;
 				return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->msdf_source_size;
+			} else if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+				if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+					return Vector2(delta.x, delta.y) * (double)p_size / (double)fd->fixed_size;
+				} else {
+					return Vector2(delta.x, delta.y) * Math::round((double)p_size / (double)fd->fixed_size);
+				}
 			} else {
 			} else {
 				return Vector2(delta.x, delta.y);
 				return Vector2(delta.x, delta.y);
 			}
 			}
@@ -2435,8 +2517,20 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 						cpos.y = Math::floor(cpos.y);
 						cpos.y = Math::floor(cpos.y);
 						cpos.x = Math::floor(cpos.x);
 						cpos.x = Math::floor(cpos.x);
 					}
 					}
-					cpos += gl.rect.position;
+					Vector2 gpos = gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
+					if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+						if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+							double gl_scale = (double)p_size / (double)fd->fixed_size;
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						} else {
+							double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						}
+					}
+					cpos += gpos;
 					if (lcd_aa) {
 					if (lcd_aa) {
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 					} else {
 					} else {
@@ -2527,8 +2621,20 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R
 						cpos.y = Math::floor(cpos.y);
 						cpos.y = Math::floor(cpos.y);
 						cpos.x = Math::floor(cpos.x);
 						cpos.x = Math::floor(cpos.x);
 					}
 					}
-					cpos += gl.rect.position;
+					Vector2 gpos = gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
+					if (fd->fixed_size > 0 && fd->fixed_size_scale_mode != FIXED_SIZE_SCALE_DISABLE && size.x != p_size) {
+						if (fd->fixed_size_scale_mode == FIXED_SIZE_SCALE_ENABLED) {
+							double gl_scale = (double)p_size / (double)fd->fixed_size;
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						} else {
+							double gl_scale = Math::round((double)p_size / (double)fd->fixed_size);
+							gpos *= gl_scale;
+							csize *= gl_scale;
+						}
+					}
+					cpos += gpos;
 					if (lcd_aa) {
 					if (lcd_aa) {
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 						RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
 					} else {
 					} else {

+ 4 - 0
modules/text_server_fb/text_server_fb.h

@@ -256,6 +256,7 @@ class TextServerFallback : public TextServerExtension {
 		TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
 		TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
 		bool mipmaps = false;
 		bool mipmaps = false;
 		bool msdf = false;
 		bool msdf = false;
+		FixedSizeScaleMode fixed_size_scale_mode = FIXED_SIZE_SCALE_DISABLE;
 		int msdf_range = 14;
 		int msdf_range = 14;
 		int msdf_source_size = 48;
 		int msdf_source_size = 48;
 		int fixed_size = 0;
 		int fixed_size = 0;
@@ -627,6 +628,9 @@ public:
 	MODBIND2(font_set_fixed_size, const RID &, int64_t);
 	MODBIND2(font_set_fixed_size, const RID &, int64_t);
 	MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
 	MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
 
 
+	MODBIND2(font_set_fixed_size_scale_mode, const RID &, FixedSizeScaleMode);
+	MODBIND1RC(FixedSizeScaleMode, font_get_fixed_size_scale_mode, const RID &);
+
 	MODBIND2(font_set_allow_system_fallback, const RID &, bool);
 	MODBIND2(font_set_allow_system_fallback, const RID &, bool);
 	MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
 	MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
 
 

+ 21 - 0
scene/resources/font.cpp

@@ -574,6 +574,7 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index, int p_make_linked_f
 			TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
 			TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range);
 			TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
 			TS->font_set_msdf_size(cache[p_cache_index], msdf_size);
 			TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
 			TS->font_set_fixed_size(cache[p_cache_index], fixed_size);
+			TS->font_set_fixed_size_scale_mode(cache[p_cache_index], fixed_size_scale_mode);
 			TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
 			TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
 			TS->font_set_allow_system_fallback(cache[p_cache_index], allow_system_fallback);
 			TS->font_set_allow_system_fallback(cache[p_cache_index], allow_system_fallback);
 			TS->font_set_hinting(cache[p_cache_index], hinting);
 			TS->font_set_hinting(cache[p_cache_index], hinting);
@@ -889,6 +890,9 @@ void FontFile::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size);
 	ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size);
 	ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size);
 	ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size);
 
 
+	ClassDB::bind_method(D_METHOD("set_fixed_size_scale_mode", "fixed_size_scale_mode"), &FontFile::set_fixed_size_scale_mode);
+	ClassDB::bind_method(D_METHOD("get_fixed_size_scale_mode"), &FontFile::get_fixed_size_scale_mode);
+
 	ClassDB::bind_method(D_METHOD("set_allow_system_fallback", "allow_system_fallback"), &FontFile::set_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("set_allow_system_fallback", "allow_system_fallback"), &FontFile::set_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("is_allow_system_fallback"), &FontFile::is_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("is_allow_system_fallback"), &FontFile::is_allow_system_fallback);
 
 
@@ -1015,6 +1019,7 @@ void FontFile::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size_scale_mode", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size_scale_mode", "get_fixed_size_scale_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides");
 	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides");
 }
 }
 
 
@@ -1368,6 +1373,7 @@ void FontFile::reset_state() {
 	msdf_pixel_range = 14;
 	msdf_pixel_range = 14;
 	msdf_size = 128;
 	msdf_size = 128;
 	fixed_size = 0;
 	fixed_size = 0;
+	fixed_size_scale_mode = TextServer::FIXED_SIZE_SCALE_DISABLE;
 	oversampling = 0.f;
 	oversampling = 0.f;
 
 
 	Font::reset_state();
 	Font::reset_state();
@@ -2144,6 +2150,21 @@ int FontFile::get_fixed_size() const {
 	return fixed_size;
 	return fixed_size;
 }
 }
 
 
+void FontFile::set_fixed_size_scale_mode(TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
+	if (fixed_size_scale_mode != p_fixed_size_scale_mode) {
+		fixed_size_scale_mode = p_fixed_size_scale_mode;
+		for (int i = 0; i < cache.size(); i++) {
+			_ensure_rid(i);
+			TS->font_set_fixed_size_scale_mode(cache[i], fixed_size_scale_mode);
+		}
+		emit_changed();
+	}
+}
+
+TextServer::FixedSizeScaleMode FontFile::get_fixed_size_scale_mode() const {
+	return fixed_size_scale_mode;
+}
+
 void FontFile::set_allow_system_fallback(bool p_allow_system_fallback) {
 void FontFile::set_allow_system_fallback(bool p_allow_system_fallback) {
 	if (allow_system_fallback != p_allow_system_fallback) {
 	if (allow_system_fallback != p_allow_system_fallback) {
 		allow_system_fallback = p_allow_system_fallback;
 		allow_system_fallback = p_allow_system_fallback;

+ 4 - 0
scene/resources/font.h

@@ -190,6 +190,7 @@ class FontFile : public Font {
 	int msdf_pixel_range = 16;
 	int msdf_pixel_range = 16;
 	int msdf_size = 48;
 	int msdf_size = 48;
 	int fixed_size = 0;
 	int fixed_size = 0;
+	TextServer::FixedSizeScaleMode fixed_size_scale_mode = TextServer::FIXED_SIZE_SCALE_DISABLE;
 	bool force_autohinter = false;
 	bool force_autohinter = false;
 	bool allow_system_fallback = true;
 	bool allow_system_fallback = true;
 	TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
 	TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
@@ -257,6 +258,9 @@ public:
 	virtual void set_fixed_size(int p_fixed_size);
 	virtual void set_fixed_size(int p_fixed_size);
 	virtual int get_fixed_size() const;
 	virtual int get_fixed_size() const;
 
 
+	virtual void set_fixed_size_scale_mode(TextServer::FixedSizeScaleMode p_fixed_size_scale_mode);
+	virtual TextServer::FixedSizeScaleMode get_fixed_size_scale_mode() const;
+
 	virtual void set_allow_system_fallback(bool p_allow_system_fallback);
 	virtual void set_allow_system_fallback(bool p_allow_system_fallback);
 	virtual bool is_allow_system_fallback() const;
 	virtual bool is_allow_system_fallback() const;
 
 

+ 13 - 0
servers/text/text_server_extension.cpp

@@ -95,6 +95,9 @@ void TextServerExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_font_set_fixed_size, "font_rid", "fixed_size");
 	GDVIRTUAL_BIND(_font_set_fixed_size, "font_rid", "fixed_size");
 	GDVIRTUAL_BIND(_font_get_fixed_size, "font_rid");
 	GDVIRTUAL_BIND(_font_get_fixed_size, "font_rid");
 
 
+	GDVIRTUAL_BIND(_font_set_fixed_size_scale_mode, "font_rid", "fixed_size_scale_mode");
+	GDVIRTUAL_BIND(_font_get_fixed_size_scale_mode, "font_rid");
+
 	GDVIRTUAL_BIND(_font_set_allow_system_fallback, "font_rid", "allow_system_fallback");
 	GDVIRTUAL_BIND(_font_set_allow_system_fallback, "font_rid", "allow_system_fallback");
 	GDVIRTUAL_BIND(_font_is_allow_system_fallback, "font_rid");
 	GDVIRTUAL_BIND(_font_is_allow_system_fallback, "font_rid");
 
 
@@ -559,6 +562,16 @@ int64_t TextServerExtension::font_get_fixed_size(const RID &p_font_rid) const {
 	return ret;
 	return ret;
 }
 }
 
 
+void TextServerExtension::font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
+	GDVIRTUAL_CALL(_font_set_fixed_size_scale_mode, p_font_rid, p_fixed_size_scale_mode);
+}
+
+TextServer::FixedSizeScaleMode TextServerExtension::font_get_fixed_size_scale_mode(const RID &p_font_rid) const {
+	FixedSizeScaleMode ret = FIXED_SIZE_SCALE_DISABLE;
+	GDVIRTUAL_CALL(_font_get_fixed_size_scale_mode, p_font_rid, ret);
+	return ret;
+}
+
 void TextServerExtension::font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 void TextServerExtension::font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
 	GDVIRTUAL_CALL(_font_set_allow_system_fallback, p_font_rid, p_allow_system_fallback);
 	GDVIRTUAL_CALL(_font_set_allow_system_fallback, p_font_rid, p_allow_system_fallback);
 }
 }

+ 5 - 0
servers/text/text_server_extension.h

@@ -153,6 +153,11 @@ public:
 	GDVIRTUAL2(_font_set_fixed_size, RID, int64_t);
 	GDVIRTUAL2(_font_set_fixed_size, RID, int64_t);
 	GDVIRTUAL1RC(int64_t, _font_get_fixed_size, RID);
 	GDVIRTUAL1RC(int64_t, _font_get_fixed_size, RID);
 
 
+	virtual void font_set_fixed_size_scale_mode(const RID &p_font_rid, FixedSizeScaleMode p_fixed_size_scale) override;
+	virtual FixedSizeScaleMode font_get_fixed_size_scale_mode(const RID &p_font_rid) const override;
+	GDVIRTUAL2(_font_set_fixed_size_scale_mode, RID, FixedSizeScaleMode);
+	GDVIRTUAL1RC(FixedSizeScaleMode, _font_get_fixed_size_scale_mode, RID);
+
 	virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override;
 	virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override;
 	virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override;
 	virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override;
 	GDVIRTUAL2(_font_set_subpixel_positioning, RID, SubpixelPositioning);
 	GDVIRTUAL2(_font_set_subpixel_positioning, RID, SubpixelPositioning);

+ 8 - 0
servers/text_server.cpp

@@ -249,6 +249,9 @@ void TextServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size);
 	ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size);
 	ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size);
 	ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size);
 
 
+	ClassDB::bind_method(D_METHOD("font_set_fixed_size_scale_mode", "font_rid", "fixed_size_scale_mode"), &TextServer::font_set_fixed_size_scale_mode);
+	ClassDB::bind_method(D_METHOD("font_get_fixed_size_scale_mode", "font_rid"), &TextServer::font_get_fixed_size_scale_mode);
+
 	ClassDB::bind_method(D_METHOD("font_set_allow_system_fallback", "font_rid", "allow_system_fallback"), &TextServer::font_set_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("font_set_allow_system_fallback", "font_rid", "allow_system_fallback"), &TextServer::font_set_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("font_is_allow_system_fallback", "font_rid"), &TextServer::font_is_allow_system_fallback);
 	ClassDB::bind_method(D_METHOD("font_is_allow_system_fallback", "font_rid"), &TextServer::font_is_allow_system_fallback);
 
 
@@ -619,6 +622,11 @@ void TextServer::_bind_methods() {
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
 	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
+
+	/* Fixed size scale mode */
+	BIND_ENUM_CONSTANT(FIXED_SIZE_SCALE_DISABLE);
+	BIND_ENUM_CONSTANT(FIXED_SIZE_SCALE_INTEGER_ONLY);
+	BIND_ENUM_CONSTANT(FIXED_SIZE_SCALE_ENABLED);
 }
 }
 
 
 Vector2 TextServer::get_hex_code_box_size(int64_t p_size, int64_t p_index) const {
 Vector2 TextServer::get_hex_code_box_size(int64_t p_size, int64_t p_index) const {

+ 10 - 0
servers/text_server.h

@@ -207,6 +207,12 @@ public:
 		STRUCTURED_TEXT_CUSTOM
 		STRUCTURED_TEXT_CUSTOM
 	};
 	};
 
 
+	enum FixedSizeScaleMode {
+		FIXED_SIZE_SCALE_DISABLE,
+		FIXED_SIZE_SCALE_INTEGER_ONLY,
+		FIXED_SIZE_SCALE_ENABLED,
+	};
+
 	void _draw_hex_code_box_number(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, uint8_t p_index, const Color &p_color) const;
 	void _draw_hex_code_box_number(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, uint8_t p_index, const Color &p_color) const;
 
 
 protected:
 protected:
@@ -281,6 +287,9 @@ public:
 	virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) = 0;
 	virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) = 0;
 	virtual int64_t font_get_fixed_size(const RID &p_font_rid) const = 0;
 	virtual int64_t font_get_fixed_size(const RID &p_font_rid) const = 0;
 
 
+	virtual void font_set_fixed_size_scale_mode(const RID &p_font_rid, FixedSizeScaleMode p_fixed_size_scale) = 0;
+	virtual FixedSizeScaleMode font_get_fixed_size_scale_mode(const RID &p_font_rid) const = 0;
+
 	virtual void font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) = 0;
 	virtual void font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) = 0;
 	virtual bool font_is_allow_system_fallback(const RID &p_font_rid) const = 0;
 	virtual bool font_is_allow_system_fallback(const RID &p_font_rid) const = 0;
 
 
@@ -620,6 +629,7 @@ VARIANT_BITFIELD_CAST(TextServer::FontStyle);
 VARIANT_ENUM_CAST(TextServer::StructuredTextParser);
 VARIANT_ENUM_CAST(TextServer::StructuredTextParser);
 VARIANT_ENUM_CAST(TextServer::FontAntialiasing);
 VARIANT_ENUM_CAST(TextServer::FontAntialiasing);
 VARIANT_ENUM_CAST(TextServer::FontLCDSubpixelLayout);
 VARIANT_ENUM_CAST(TextServer::FontLCDSubpixelLayout);
+VARIANT_ENUM_CAST(TextServer::FixedSizeScaleMode);
 
 
 GDVIRTUAL_NATIVE_PTR(Glyph);
 GDVIRTUAL_NATIVE_PTR(Glyph);
 GDVIRTUAL_NATIVE_PTR(CaretInfo);
 GDVIRTUAL_NATIVE_PTR(CaretInfo);