Переглянути джерело

Merge pull request #57877 from bruvzg/subpixel_gl_pos

Rémi Verschelde 3 роки тому
батько
коміт
760a95e86a

+ 3 - 0
doc/classes/FontData.xml

@@ -600,5 +600,8 @@
 		<member name="style_name" type="String" setter="set_font_style_name" getter="get_font_style_name" default="&quot;&quot;">
 		<member name="style_name" type="String" setter="set_font_style_name" getter="get_font_style_name" default="&quot;&quot;">
 			Font style name.
 			Font style name.
 		</member>
 		</member>
+		<member name="subpixel_positioning" type="int" setter="set_subpixel_positioning" getter="get_subpixel_positioning" enum="TextServer.SubpixelPositioning" default="1">
+			Font glyph sub-pixel positioning mode. Subpixel positioning provides shaper text and better kerning for smaller font sizes, at the cost of memory usage and font rasterization speed. Use [constant TextServer.SUBPIXEL_POSITIONING_AUTO] to automatically enable it based on the font size.
+		</member>
 	</members>
 	</members>
 </class>
 </class>

+ 9 - 0
doc/classes/ProjectSettings.xml

@@ -564,6 +564,15 @@
 		<member name="gui/theme/custom_font" type="String" setter="" getter="" default="&quot;&quot;">
 		<member name="gui/theme/custom_font" type="String" setter="" getter="" default="&quot;&quot;">
 			Path to a custom [Font] resource to use as default for all GUI elements of the project.
 			Path to a custom [Font] resource to use as default for all GUI elements of the project.
 		</member>
 		</member>
+		<member name="gui/theme/default_font_antialiased" type="bool" setter="" getter="" default="true">
+			If set to [code]true[/code], default font uses 8-bit anitialiased glyph rendering.  See [member FontData.antialiased].
+		</member>
+		<member name="gui/theme/default_font_hinting" type="int" setter="" getter="" default="1">
+			Default font hinting mode. See [member FontData.hinting].
+		</member>
+		<member name="gui/theme/default_font_subpixel_positioning" type="int" setter="" getter="" default="1">
+			Default font glyph sub-pixel positioning mode. See [member FontData.subpixel_positioning].
+		</member>
 		<member name="gui/theme/default_theme_scale" type="float" setter="" getter="" default="1.0">
 		<member name="gui/theme/default_theme_scale" type="float" setter="" getter="" default="1.0">
 		</member>
 		</member>
 		<member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="" default="2000">
 		<member name="gui/timers/incremental_search_max_interval_msec" type="int" setter="" getter="" default="2000">

+ 32 - 2
doc/classes/TextServer.xml

@@ -328,6 +328,13 @@
 				Returns font style name.
 				Returns font style name.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="font_get_subpixel_positioning" qualifiers="const">
+			<return type="int" enum="TextServer.SubpixelPositioning" />
+			<argument index="0" name="font_rid" type="RID" />
+			<description>
+				Returns font sub-pixel glyph positioning mode.
+			</description>
+		</method>
 		<method name="font_get_supported_chars" qualifiers="const">
 		<method name="font_get_supported_chars" qualifiers="const">
 			<return type="String" />
 			<return type="String" />
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="0" name="font_rid" type="RID" />
@@ -613,7 +620,7 @@
 		<method name="font_set_hinting">
 		<method name="font_set_hinting">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="0" name="font_rid" type="RID" />
-			<argument index="1" name="_hinting" type="int" enum="TextServer.Hinting" />
+			<argument index="1" name="hinting" type="int" enum="TextServer.Hinting" />
 			<description>
 			<description>
 				Sets font hinting mode. Used by dynamic fonts only.
 				Sets font hinting mode. Used by dynamic fonts only.
 			</description>
 			</description>
@@ -727,7 +734,15 @@
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="1" name="name" type="String" />
 			<argument index="1" name="name" type="String" />
 			<description>
 			<description>
-				Set the font style name.
+				Sets the font style name.
+			</description>
+		</method>
+		<method name="font_set_subpixel_positioning">
+			<return type="void" />
+			<argument index="0" name="font_rid" type="RID" />
+			<argument index="1" name="subpixel_positioning" type="int" enum="TextServer.SubpixelPositioning" />
+			<description>
+				Sets font sub-pixel glyph positioning mode.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="font_set_texture_image">
 		<method name="font_set_texture_image">
@@ -1493,6 +1508,21 @@
 		<constant name="HINTING_NORMAL" value="2" enum="Hinting">
 		<constant name="HINTING_NORMAL" value="2" enum="Hinting">
 			Use the default font hinting mode (crisper but less smooth).
 			Use the default font hinting mode (crisper but less smooth).
 		</constant>
 		</constant>
+		<constant name="SUBPIXEL_POSITIONING_DISABLED" value="0" enum="SubpixelPositioning">
+			Glyph horizontal position is rounded to the whole pixel size, each glyph is rasterized once.
+		</constant>
+		<constant name="SUBPIXEL_POSITIONING_AUTO" value="1" enum="SubpixelPositioning">
+			Glyph horizontal position is rounded based on font size.
+			- To one quarter of the pixel size if font size is smaller or equal to [code]16[/code].
+			- To one half of the pixel size if font size is smaller or equal to [code]20[/code].
+			- To the whole pixel size for larger fonts.
+		</constant>
+		<constant name="SUBPIXEL_POSITIONING_ONE_HALF" value="2" enum="SubpixelPositioning">
+			Glyph horizontal position is rounded to one half of the pixel size, each glyph is rasterized up to two times.
+		</constant>
+		<constant name="SUBPIXEL_POSITIONING_ONE_QUARTER" value="3" enum="SubpixelPositioning">
+			Glyph horizontal position is rounded to one quarter of the pixel size, each glyph is rasterized up to four times.
+		</constant>
 		<constant name="FEATURE_BIDI_LAYOUT" value="1" enum="Feature">
 		<constant name="FEATURE_BIDI_LAYOUT" value="1" enum="Feature">
 			TextServer supports bidirectional layouts.
 			TextServer supports bidirectional layouts.
 		</constant>
 		</constant>

+ 15 - 0
doc/classes/TextServerExtension.xml

@@ -328,6 +328,13 @@
 				Returns font style name.
 				Returns font style name.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_font_get_subpixel_positioning" qualifiers="virtual const">
+			<return type="int" enum="TextServer.SubpixelPositioning" />
+			<argument index="0" name="font_rid" type="RID" />
+			<description>
+				Returns font sub-pixel glyph positioning mode.
+			</description>
+		</method>
 		<method name="_font_get_supported_chars" qualifiers="virtual const">
 		<method name="_font_get_supported_chars" qualifiers="virtual const">
 			<return type="String" />
 			<return type="String" />
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="0" name="font_rid" type="RID" />
@@ -738,6 +745,14 @@
 				Sets the font style name.
 				Sets the font style name.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="_font_set_subpixel_positioning" qualifiers="virtual">
+			<return type="void" />
+			<argument index="0" name="font_rid" type="RID" />
+			<argument index="1" name="subpixel_positioning" type="int" enum="TextServer.SubpixelPositioning" />
+			<description>
+				Sets font sub-pixel glyph positioning mode.
+			</description>
+		</method>
 		<method name="_font_set_texture_image" qualifiers="virtual">
 		<method name="_font_set_texture_image" qualifiers="virtual">
 			<return type="void" />
 			<return type="void" />
 			<argument index="0" name="font_rid" type="RID" />
 			<argument index="0" name="font_rid" type="RID" />

+ 3 - 3
editor/animation_track_editor.cpp

@@ -1576,10 +1576,10 @@ void AnimationTimelineEdit::_notification(int p_what) {
 		int decimals = 2;
 		int decimals = 2;
 		bool step_found = false;
 		bool step_found = false;
 
 
-		const int period_width = font->get_char_size('.', 0, font_size).width;
-		int max_digit_width = font->get_char_size('0', 0, font_size).width;
+		const float period_width = font->get_char_size('.', 0, font_size).width;
+		float max_digit_width = font->get_char_size('0', 0, font_size).width;
 		for (int i = 1; i <= 9; i++) {
 		for (int i = 1; i <= 9; i++) {
-			const int digit_width = font->get_char_size('0' + i, 0, font_size).width;
+			const float digit_width = font->get_char_size('0' + i, 0, font_size).width;
 			max_digit_width = MAX(digit_width, max_digit_width);
 			max_digit_width = MAX(digit_width, max_digit_width);
 		}
 		}
 		const int max_sc = int(Math::ceil(zoomw / scale));
 		const int max_sc = int(Math::ceil(zoomw / scale));

+ 35 - 32
editor/editor_fonts.cpp

@@ -145,7 +145,7 @@
 	m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE);        \
 	m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE);        \
 	MAKE_FALLBACKS(m_name);
 	MAKE_FALLBACKS(m_name);
 
 
-Ref<FontData> load_cached_external_font(const String &p_path, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) {
+Ref<FontData> load_cached_external_font(const String &p_path, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning) {
 	Ref<FontData> font;
 	Ref<FontData> font;
 	font.instantiate();
 	font.instantiate();
 
 
@@ -155,11 +155,12 @@ Ref<FontData> load_cached_external_font(const String &p_path, TextServer::Hintin
 	font->set_antialiased(p_aa);
 	font->set_antialiased(p_aa);
 	font->set_hinting(p_hinting);
 	font->set_hinting(p_hinting);
 	font->set_force_autohinter(p_autohint);
 	font->set_force_autohinter(p_autohint);
+	font->set_subpixel_positioning(p_font_subpixel_positioning);
 
 
 	return font;
 	return font;
 }
 }
 
 
-Ref<FontData> load_cached_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint) {
+Ref<FontData> load_cached_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning) {
 	Ref<FontData> font;
 	Ref<FontData> font;
 	font.instantiate();
 	font.instantiate();
 
 
@@ -167,6 +168,7 @@ Ref<FontData> load_cached_internal_font(const uint8_t *p_data, size_t p_size, Te
 	font->set_antialiased(p_aa);
 	font->set_antialiased(p_aa);
 	font->set_hinting(p_hinting);
 	font->set_hinting(p_hinting);
 	font->set_force_autohinter(p_autohint);
 	font->set_force_autohinter(p_autohint);
+	font->set_subpixel_positioning(p_font_subpixel_positioning);
 
 
 	return font;
 	return font;
 }
 }
@@ -178,6 +180,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
 
 
 	bool font_antialiased = (bool)EditorSettings::get_singleton()->get("interface/editor/font_antialiased");
 	bool font_antialiased = (bool)EditorSettings::get_singleton()->get("interface/editor/font_antialiased");
 	int font_hinting_setting = (int)EditorSettings::get_singleton()->get("interface/editor/font_hinting");
 	int font_hinting_setting = (int)EditorSettings::get_singleton()->get("interface/editor/font_hinting");
+	TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EditorSettings::get_singleton()->get("interface/editor/font_subpixel_positioning");
 
 
 	TextServer::Hinting font_hinting;
 	TextServer::Hinting font_hinting;
 	switch (font_hinting_setting) {
 	switch (font_hinting_setting) {
@@ -208,7 +211,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
 	String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
 	String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
 	Ref<FontData> CustomFont;
 	Ref<FontData> CustomFont;
 	if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
 	if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
-		CustomFont = load_cached_external_font(custom_font_path, font_hinting, font_antialiased, true);
+		CustomFont = load_cached_external_font(custom_font_path, font_hinting, font_antialiased, true, font_subpixel_positioning);
 	} else {
 	} else {
 		EditorSettings::get_singleton()->set_manually("interface/editor/main_font", "");
 		EditorSettings::get_singleton()->set_manually("interface/editor/main_font", "");
 	}
 	}
@@ -218,7 +221,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
 	String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold");
 	String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold");
 	Ref<FontData> CustomFontBold;
 	Ref<FontData> CustomFontBold;
 	if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
 	if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
-		CustomFontBold = load_cached_external_font(custom_font_path_bold, font_hinting, font_antialiased, true);
+		CustomFontBold = load_cached_external_font(custom_font_path_bold, font_hinting, font_antialiased, true, font_subpixel_positioning);
 	} else {
 	} else {
 		EditorSettings::get_singleton()->set_manually("interface/editor/main_font_bold", "");
 		EditorSettings::get_singleton()->set_manually("interface/editor/main_font_bold", "");
 	}
 	}
@@ -228,7 +231,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
 	String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
 	String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
 	Ref<FontData> CustomFontSource;
 	Ref<FontData> CustomFontSource;
 	if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) {
 	if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) {
-		CustomFontSource = load_cached_external_font(custom_font_path_source, font_hinting, font_antialiased, true);
+		CustomFontSource = load_cached_external_font(custom_font_path_source, font_hinting, font_antialiased, true, font_subpixel_positioning);
 	} else {
 	} else {
 		EditorSettings::get_singleton()->set_manually("interface/editor/code_font", "");
 		EditorSettings::get_singleton()->set_manually("interface/editor/code_font", "");
 	}
 	}
@@ -237,39 +240,39 @@ void editor_register_fonts(Ref<Theme> p_theme) {
 
 
 	/* Noto Sans */
 	/* Noto Sans */
 
 
-	Ref<FontData> DefaultFont = load_cached_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> DefaultFontBold = load_cached_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontArabic = load_cached_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontArabicBold = load_cached_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontBengali = load_cached_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontBengaliBold = load_cached_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontDevanagari = load_cached_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontDevanagariBold = load_cached_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontGeorgian = load_cached_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontGeorgianBold = load_cached_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontHebrew = load_cached_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontHebrewBold = load_cached_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontMalayalam = load_cached_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontMalayalamBold = load_cached_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontOriya = load_cached_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontOriyaBold = load_cached_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontSinhala = load_cached_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontSinhalaBold = load_cached_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontTamil = load_cached_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontTamilBold = load_cached_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontTelugu = load_cached_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontTeluguBold = load_cached_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontThai = load_cached_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontThaiBold = load_cached_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiased, true);
+	Ref<FontData> DefaultFont = load_cached_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> DefaultFontBold = load_cached_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontArabic = load_cached_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontArabicBold = load_cached_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontBengali = load_cached_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontBengaliBold = load_cached_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontDevanagari = load_cached_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontDevanagariBold = load_cached_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontGeorgian = load_cached_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontGeorgianBold = load_cached_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontHebrew = load_cached_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontHebrewBold = load_cached_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontMalayalam = load_cached_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontMalayalamBold = load_cached_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontOriya = load_cached_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontOriyaBold = load_cached_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontSinhala = load_cached_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontSinhalaBold = load_cached_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontTamil = load_cached_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontTamilBold = load_cached_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontTelugu = load_cached_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontTeluguBold = load_cached_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontThai = load_cached_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontThaiBold = load_cached_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
 
 
 	/* Droid Sans */
 	/* Droid Sans */
 
 
-	Ref<FontData> FontFallback = load_cached_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiased, true);
-	Ref<FontData> FontJapanese = load_cached_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiased, true);
+	Ref<FontData> FontFallback = load_cached_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
+	Ref<FontData> FontJapanese = load_cached_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
 
 
 	/* Hack */
 	/* Hack */
 
 
-	Ref<FontData> dfmono = load_cached_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiased, true);
+	Ref<FontData> dfmono = load_cached_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning);
 	Dictionary opentype_features;
 	Dictionary opentype_features;
 	opentype_features["calt"] = 0;
 	opentype_features["calt"] = 0;
 	dfmono->set_opentype_feature_overrides(opentype_features); // Disable contextual alternates (coding ligatures).
 	dfmono->set_opentype_feature_overrides(opentype_features); // Disable contextual alternates (coding ligatures).

+ 2 - 0
editor/editor_settings.cpp

@@ -415,6 +415,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 #else
 #else
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_hinting", 0, "Auto (Light),None,Light,Normal")
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_hinting", 0, "Auto (Light),None,Light,Normal")
 #endif
 #endif
+	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_subpixel_positioning", 1, "Disabled,Auto,One half of a pixel,One quarter of a pixel")
+
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf")
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf")
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf")
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf")
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/code_font", "", "*.ttf,*.otf")
 	EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/code_font", "", "*.ttf,*.otf")

+ 7 - 0
editor/import/dynamicfont_import_settings.cpp

@@ -480,6 +480,10 @@ void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_proper
 		if (font_preview->get_data_count() > 0) {
 		if (font_preview->get_data_count() > 0) {
 			font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
 			font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
 		}
 		}
+	} else if (p_edited_property == "subpixel_positioning") {
+		if (font_preview->get_data_count() > 0) {
+			font_preview->get_data(0)->set_subpixel_positioning((TextServer::SubpixelPositioning)import_settings_data->get("subpixel_positioning").operator int());
+		}
 	} else if (p_edited_property == "oversampling") {
 	} else if (p_edited_property == "oversampling") {
 		if (font_preview->get_data_count() > 0) {
 		if (font_preview->get_data_count() > 0) {
 			font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
 			font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
@@ -915,6 +919,7 @@ void DynamicFontImportSettings::_re_import() {
 	main_settings["msdf_size"] = import_settings_data->get("msdf_size");
 	main_settings["msdf_size"] = import_settings_data->get("msdf_size");
 	main_settings["force_autohinter"] = import_settings_data->get("force_autohinter");
 	main_settings["force_autohinter"] = import_settings_data->get("force_autohinter");
 	main_settings["hinting"] = import_settings_data->get("hinting");
 	main_settings["hinting"] = import_settings_data->get("hinting");
+	main_settings["subpixel_positioning"] = import_settings_data->get("subpixel_positioning");
 	main_settings["oversampling"] = import_settings_data->get("oversampling");
 	main_settings["oversampling"] = import_settings_data->get("oversampling");
 	main_settings["compress"] = import_settings_data->get("compress");
 	main_settings["compress"] = import_settings_data->get("compress");
 
 
@@ -1265,6 +1270,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
 		font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
 		font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
 		font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
 		font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
 		font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
 		font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
+		font_preview->get_data(0)->set_subpixel_positioning((TextServer::SubpixelPositioning)import_settings_data->get("subpixel_positioning").operator int());
 		font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
 		font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
 	}
 	}
 	font_preview_label->add_theme_font_override("font", font_preview);
 	font_preview_label->add_theme_font_override("font", font_preview);
@@ -1323,6 +1329,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
+	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), 1));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "compress", PROPERTY_HINT_NONE, ""), false));
 	options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "compress", PROPERTY_HINT_NONE, ""), false));
 
 

+ 6 - 0
editor/import/resource_importer_dynamicfont.cpp

@@ -76,6 +76,9 @@ bool ResourceImporterDynamicFont::get_option_visibility(const String &p_path, co
 	if (p_option == "oversampling" && bool(p_options["multichannel_signed_distance_field"])) {
 	if (p_option == "oversampling" && bool(p_options["multichannel_signed_distance_field"])) {
 		return false;
 		return false;
 	}
 	}
+	if (p_option == "subpixel_positioning" && bool(p_options["multichannel_signed_distance_field"])) {
+		return false;
+	}
 	return true;
 	return true;
 }
 }
 
 
@@ -104,6 +107,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<
 
 
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), 1));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
 
 
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
@@ -179,6 +183,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
 
 
 	bool autohinter = p_options["force_autohinter"];
 	bool autohinter = p_options["force_autohinter"];
 	int hinting = p_options["hinting"];
 	int hinting = p_options["hinting"];
+	int subpixel_positioning = p_options["subpixel_positioning"];
 	real_t oversampling = p_options["oversampling"];
 	real_t oversampling = p_options["oversampling"];
 
 
 	// Load base font data.
 	// Load base font data.
@@ -195,6 +200,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
 	font->set_opentype_feature_overrides(ot_ov);
 	font->set_opentype_feature_overrides(ot_ov);
 	font->set_fixed_size(0);
 	font->set_fixed_size(0);
 	font->set_force_autohinter(autohinter);
 	font->set_force_autohinter(autohinter);
+	font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
 	font->set_hinting((TextServer::Hinting)hinting);
 	font->set_hinting((TextServer::Hinting)hinting);
 	font->set_oversampling(oversampling);
 	font->set_oversampling(oversampling);
 
 

+ 1 - 0
editor/import/resource_importer_imagefont.cpp

@@ -98,6 +98,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
 	font->set_antialiased(false);
 	font->set_antialiased(false);
 	font->set_multichannel_signed_distance_field(false);
 	font->set_multichannel_signed_distance_field(false);
 	font->set_fixed_size(base_size);
 	font->set_fixed_size(base_size);
+	font->set_subpixel_positioning(TextServer::SUBPIXEL_POSITIONING_DISABLED);
 	font->set_force_autohinter(false);
 	font->set_force_autohinter(false);
 	font->set_hinting(TextServer::HINTING_NONE);
 	font->set_hinting(TextServer::HINTING_NONE);
 	font->set_oversampling(1.0f);
 	font->set_oversampling(1.0f);

+ 151 - 23
modules/text_server_adv/text_server_adv.cpp

@@ -1097,12 +1097,14 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
 _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
 _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
 	ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
 	ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
 
 
+	int32_t glyph_index = p_glyph & 0xFFFFFF; // Remove subpixel shifts.
+
 	FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
 	FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
 	if (fd->glyph_map.has(p_glyph)) {
 	if (fd->glyph_map.has(p_glyph)) {
 		return fd->glyph_map[p_glyph].found;
 		return fd->glyph_map[p_glyph].found;
 	}
 	}
 
 
-	if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+	if (glyph_index == 0) { // Non graphical or invalid glyph, do not render.
 		fd->glyph_map[p_glyph] = FontGlyph();
 		fd->glyph_map[p_glyph] = FontGlyph();
 		return true;
 		return true;
 	}
 	}
@@ -1134,15 +1136,25 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
 		}
 		}
 
 
 		FT_Fixed v, h;
 		FT_Fixed v, h;
-		FT_Get_Advance(fd->face, p_glyph, flags, &h);
-		FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
+		FT_Get_Advance(fd->face, glyph_index, flags, &h);
+		FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
 
 
-		int error = FT_Load_Glyph(fd->face, p_glyph, flags);
+		int error = FT_Load_Glyph(fd->face, glyph_index, flags);
 		if (error) {
 		if (error) {
 			fd->glyph_map[p_glyph] = FontGlyph();
 			fd->glyph_map[p_glyph] = FontGlyph();
 			return false;
 			return false;
 		}
 		}
 
 
+		if (!p_font_data->msdf) {
+			if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+				FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 4;
+				FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);
+			} else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+				FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 5;
+				FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);
+			}
+		}
+
 		if (!outline) {
 		if (!outline) {
 			if (!p_font_data->msdf) {
 			if (!p_font_data->msdf) {
 				error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
 				error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
@@ -1844,6 +1856,24 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const {
 	return fd->hinting;
 	return fd->hinting;
 }
 }
 
 
+void TextServerAdvanced::font_set_subpixel_positioning(RID p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
+	FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+	ERR_FAIL_COND(!fd);
+
+	MutexLock lock(fd->mutex);
+	if (fd->subpixel_positioning != p_subpixel) {
+		fd->subpixel_positioning = p_subpixel;
+	}
+}
+
+TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positioning(RID p_font_rid) const {
+	FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+	ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
+
+	MutexLock lock(fd->mutex);
+	return fd->subpixel_positioning;
+}
+
 void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 	FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
 	FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
 	ERR_FAIL_COND(!fd);
 	ERR_FAIL_COND(!fd);
@@ -2261,6 +2291,8 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, i
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
 		return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
+	} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+		return gl[p_glyph].advance.round();
 	} else {
 	} else {
 		return gl[p_glyph].advance;
 		return gl[p_glyph].advance;
 	}
 	}
@@ -2628,12 +2660,25 @@ void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_siz
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	for (char32_t i = p_start; i <= p_end; i++) {
 	for (char32_t i = p_start; i <= p_end; i++) {
 #ifdef MODULE_FREETYPE_ENABLED
 #ifdef MODULE_FREETYPE_ENABLED
+		int32_t idx = FT_Get_Char_Index(fd->cache[size]->face, i);
 		if (fd->cache[size]->face) {
 		if (fd->cache[size]->face) {
-			_ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i));
-			continue;
+			if (fd->msdf) {
+				_ensure_glyph(fd, size, (int32_t)idx);
+			} else {
+				if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+					_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
+				} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+					_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+				} else {
+					_ensure_glyph(fd, size, (int32_t)idx);
+				}
+			}
 		}
 		}
 #endif
 #endif
-		_ensure_glyph(fd, size, (int32_t)i);
 	}
 	}
 }
 }
 
 
@@ -2644,7 +2689,26 @@ void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_siz
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size_outline(fd, p_size);
 	Vector2i size = _get_size_outline(fd, p_size);
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index));
+#ifdef MODULE_FREETYPE_ENABLED
+	int32_t idx = p_index;
+	if (fd->cache[size]->face) {
+		if (fd->msdf) {
+			_ensure_glyph(fd, size, (int32_t)idx);
+		} else {
+			if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+				_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
+			} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+				_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+			} else {
+				_ensure_glyph(fd, size, (int32_t)idx);
+			}
+		}
+	}
+#endif
 }
 }
 
 
 void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
 void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
@@ -2654,11 +2718,26 @@ void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size(fd, p_size);
 	Vector2i size = _get_size(fd, p_size);
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	if (!_ensure_glyph(fd, size, p_index)) {
+
+	int32_t index = p_index;
+
+#ifdef MODULE_FREETYPE_ENABLED
+	if (!fd->msdf && fd->cache[size]->face) {
+		if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
+			index = index | (xshift << 27);
+		} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));
+			index = index | (xshift << 27);
+		}
+	}
+#endif
+
+	if (!_ensure_glyph(fd, size, index)) {
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 	}
 	}
 
 
-	const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+	const FontGlyph &gl = fd->cache[size]->glyph_map[index];
 	if (gl.found) {
 	if (gl.found) {
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 
 
@@ -2677,7 +2756,15 @@ void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
 				} else {
 				} else {
-					Point2 cpos = p_pos.floor();
+					Point2 cpos = p_pos;
+					cpos.y = Math::floor(cpos.y);
+					if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.125));
+					} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.25));
+					} else {
+						cpos.x = Math::floor(cpos.x);
+					}
 					cpos += gl.rect.position;
 					cpos += gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
@@ -2694,11 +2781,26 @@ void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
 	Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	if (!_ensure_glyph(fd, size, p_index)) {
+
+	int32_t index = p_index;
+
+#ifdef MODULE_FREETYPE_ENABLED
+	if (!fd->msdf && fd->cache[size]->face) {
+		if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
+			index = index | (xshift << 27);
+		} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));
+			index = index | (xshift << 27);
+		}
+	}
+#endif
+
+	if (!_ensure_glyph(fd, size, index)) {
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 	}
 	}
 
 
-	const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+	const FontGlyph &gl = fd->cache[size]->glyph_map[index];
 	if (gl.found) {
 	if (gl.found) {
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 
 
@@ -2717,7 +2819,15 @@ void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
 				} else {
 				} else {
-					Point2 cpos = p_pos.floor();
+					Point2 cpos = p_pos;
+					cpos.y = Math::floor(cpos.y);
+					if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.125));
+					} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.25));
+					} else {
+						cpos.x = Math::floor(cpos.x);
+					}
 					cpos += gl.rect.position;
 					cpos += gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
@@ -3559,7 +3669,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
 			}
 			}
 			justification_width = sd->width_trimmed;
 			justification_width = sd->width_trimmed;
 		} else {
 		} else {
-			return sd->width;
+			return Math::ceil(sd->width);
 		}
 		}
 	} else {
 	} else {
 		justification_width = sd->width;
 		justification_width = sd->width;
@@ -3662,7 +3772,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
 		sd->width = justification_width;
 		sd->width = justification_width;
 	}
 	}
 
 
-	return justification_width;
+	return Math::ceil(justification_width);
 }
 }
 
 
 float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
 float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
@@ -4281,6 +4391,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
 
 
 Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
 Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
 	hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size);
 	hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size);
+	bool subpos = (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
 	ERR_FAIL_COND_V(hb_font == nullptr, Glyph());
 	ERR_FAIL_COND_V(hb_font == nullptr, Glyph());
 
 
 	hb_buffer_clear_contents(p_sd->hb_buffer);
 	hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -4308,14 +4419,22 @@ Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char
 	if (glyph_count > 0) {
 	if (glyph_count > 0) {
 		float scale = font_get_scale(p_font, p_font_size);
 		float scale = font_get_scale(p_font, p_font_size);
 		if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
 		if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-			gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale));
+			if (subpos) {
+				gl.advance = glyph_pos[0].x_advance / (64.0 / scale);
+			} else {
+				gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale));
+			}
 		} else {
 		} else {
 			gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale));
 			gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale));
 		}
 		}
 		gl.count = 1;
 		gl.count = 1;
 
 
 		gl.index = glyph_info[0].codepoint;
 		gl.index = glyph_info[0].codepoint;
-		gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale));
+		if (subpos) {
+			gl.x_off = glyph_pos[0].x_offset / (64.0 / scale);
+		} else {
+			gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale));
+		}
 		gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale));
 		gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale));
 
 
 		if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
 		if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
@@ -4380,6 +4499,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 	float scale = font_get_scale(f, fs);
 	float scale = font_get_scale(f, fs);
 	float sp_sp = font_get_spacing(f, fs, SPACING_SPACE);
 	float sp_sp = font_get_spacing(f, fs, SPACING_SPACE);
 	float sp_gl = font_get_spacing(f, fs, SPACING_GLYPH);
 	float sp_gl = font_get_spacing(f, fs, SPACING_GLYPH);
+	bool subpos = (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
 	ERR_FAIL_COND(hb_font == nullptr);
 	ERR_FAIL_COND(hb_font == nullptr);
 
 
 	hb_buffer_clear_contents(p_sd->hb_buffer);
 	hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -4456,11 +4576,19 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
 			gl.index = glyph_info[i].codepoint;
 			gl.index = glyph_info[i].codepoint;
 			if (gl.index != 0) {
 			if (gl.index != 0) {
 				if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
 				if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
-					gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale));
+					if (subpos) {
+						gl.advance = glyph_pos[i].x_advance / (64.0 / scale);
+					} else {
+						gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale));
+					}
 				} else {
 				} else {
 					gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale));
 					gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale));
 				}
 				}
-				gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale));
+				if (subpos) {
+					gl.x_off = glyph_pos[i].x_offset / (64.0 / scale);
+				} else {
+					gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale));
+				}
 				gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
 				gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
 			}
 			}
 			if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
 			if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
@@ -4797,9 +4925,9 @@ Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
 		const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
 		const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
 	}
 	}
 	if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
 	if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
-		return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent);
+		return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent).ceil();
 	} else {
 	} else {
-		return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width));
+		return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width)).ceil();
 	}
 	}
 }
 }
 
 
@@ -4833,7 +4961,7 @@ float TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
 	if (!sd->valid) {
 	if (!sd->valid) {
 		const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
 		const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
 	}
 	}
-	return (sd->text_trimmed ? sd->width_trimmed : sd->width);
+	return Math::ceil(sd->text_trimmed ? sd->width_trimmed : sd->width);
 }
 }
 
 
 float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
 float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {

+ 4 - 0
modules/text_server_adv/text_server_adv.h

@@ -174,6 +174,7 @@ class TextServerAdvanced : public TextServer {
 		int fixed_size = 0;
 		int fixed_size = 0;
 		bool force_autohinter = false;
 		bool force_autohinter = false;
 		TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
 		TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+		TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
 		Dictionary variation_coordinates;
 		Dictionary variation_coordinates;
 		float oversampling = 0.f;
 		float oversampling = 0.f;
 
 
@@ -379,6 +380,9 @@ public:
 	virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
 	virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
 	virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
 	virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
 
 
+	virtual void font_set_subpixel_positioning(RID p_font_rid, SubpixelPositioning p_subpixel) override;
+	virtual SubpixelPositioning font_get_subpixel_positioning(RID p_font_rid) const override;
+
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 
 

+ 134 - 16
modules/text_server_fb/text_server_fb.cpp

@@ -548,12 +548,14 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
 _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
 _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
 	ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
 	ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
 
 
+	int32_t glyph_index = p_glyph & 0xFFFFFF; // Remove subpixel shifts.
+
 	FontDataForSizeFallback *fd = p_font_data->cache[p_size];
 	FontDataForSizeFallback *fd = p_font_data->cache[p_size];
 	if (fd->glyph_map.has(p_glyph)) {
 	if (fd->glyph_map.has(p_glyph)) {
 		return fd->glyph_map[p_glyph].found;
 		return fd->glyph_map[p_glyph].found;
 	}
 	}
 
 
-	if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+	if (glyph_index == 0) { // Non graphical or invalid glyph, do not render.
 		fd->glyph_map[p_glyph] = FontGlyph();
 		fd->glyph_map[p_glyph] = FontGlyph();
 		return true;
 		return true;
 	}
 	}
@@ -584,8 +586,6 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
 			flags |= FT_LOAD_COLOR;
 			flags |= FT_LOAD_COLOR;
 		}
 		}
 
 
-		int32_t glyph_index = FT_Get_Char_Index(fd->face, p_glyph);
-
 		FT_Fixed v, h;
 		FT_Fixed v, h;
 		FT_Get_Advance(fd->face, glyph_index, flags, &h);
 		FT_Get_Advance(fd->face, glyph_index, flags, &h);
 		FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
 		FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
@@ -596,6 +596,16 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
 			return false;
 			return false;
 		}
 		}
 
 
+		if (!p_font_data->msdf) {
+			if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+				FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 4;
+				FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);
+			} else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+				FT_Pos xshift = (int)((p_glyph >> 27) & 3) << 5;
+				FT_Outline_Translate(&fd->face->glyph->outline, xshift, 0);
+			}
+		}
+
 		if (!outline) {
 		if (!outline) {
 			if (!p_font_data->msdf) {
 			if (!p_font_data->msdf) {
 				error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
 				error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
@@ -1016,6 +1026,24 @@ TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const {
 	return fd->hinting;
 	return fd->hinting;
 }
 }
 
 
+void TextServerFallback::font_set_subpixel_positioning(RID p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
+	FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+	ERR_FAIL_COND(!fd);
+
+	MutexLock lock(fd->mutex);
+	if (fd->subpixel_positioning != p_subpixel) {
+		fd->subpixel_positioning = p_subpixel;
+	}
+}
+
+TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positioning(RID p_font_rid) const {
+	FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+	ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
+
+	MutexLock lock(fd->mutex);
+	return fd->subpixel_positioning;
+}
+
 void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 	FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
 	FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
 	ERR_FAIL_COND(!fd);
 	ERR_FAIL_COND(!fd);
@@ -1433,6 +1461,8 @@ Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, i
 
 
 	if (fd->msdf) {
 	if (fd->msdf) {
 		return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
 		return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
+	} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+		return gl[p_glyph].advance.round();
 	} else {
 	} else {
 		return gl[p_glyph].advance;
 		return gl[p_glyph].advance;
 	}
 	}
@@ -1781,7 +1811,26 @@ void TextServerFallback::font_render_range(RID p_font_rid, const Vector2i &p_siz
 	Vector2i size = _get_size_outline(fd, p_size);
 	Vector2i size = _get_size_outline(fd, p_size);
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	for (char32_t i = p_start; i <= p_end; i++) {
 	for (char32_t i = p_start; i <= p_end; i++) {
-		_ensure_glyph(fd, size, (int32_t)i);
+#ifdef MODULE_FREETYPE_ENABLED
+		int32_t idx = i;
+		if (fd->cache[size]->face) {
+			if (fd->msdf) {
+				_ensure_glyph(fd, size, (int32_t)idx);
+			} else {
+				if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+					_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
+				} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+					_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+					_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+				} else {
+					_ensure_glyph(fd, size, (int32_t)idx);
+				}
+			}
+		}
+#endif
 	}
 	}
 }
 }
 
 
@@ -1792,7 +1841,26 @@ void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_siz
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size_outline(fd, p_size);
 	Vector2i size = _get_size_outline(fd, p_size);
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	ERR_FAIL_COND(!_ensure_glyph(fd, size, p_index));
+#ifdef MODULE_FREETYPE_ENABLED
+	int32_t idx = p_index;
+	if (fd->cache[size]->face) {
+		if (fd->msdf) {
+			_ensure_glyph(fd, size, (int32_t)idx);
+		} else {
+			if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+				_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
+			} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+				_ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
+				_ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
+			} else {
+				_ensure_glyph(fd, size, (int32_t)idx);
+			}
+		}
+	}
+#endif
 }
 }
 
 
 void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
 void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
@@ -1802,11 +1870,26 @@ void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size(fd, p_size);
 	Vector2i size = _get_size(fd, p_size);
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	if (!_ensure_glyph(fd, size, p_index)) {
+
+	int32_t index = p_index;
+
+#ifdef MODULE_FREETYPE_ENABLED
+	if (!fd->msdf && fd->cache[size]->face) {
+		if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
+			index = index | (xshift << 27);
+		} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));
+			index = index | (xshift << 27);
+		}
+	}
+#endif
+
+	if (!_ensure_glyph(fd, size, index)) {
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 	}
 	}
 
 
-	const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+	const FontGlyph &gl = fd->cache[size]->glyph_map[index];
 	if (gl.found) {
 	if (gl.found) {
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 
 
@@ -1825,7 +1908,15 @@ void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
 				} else {
 				} else {
-					Point2 cpos = p_pos.floor();
+					Point2 cpos = p_pos;
+					cpos.y = Math::floor(cpos.y);
+					if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.125));
+					} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.25));
+					} else {
+						cpos.x = Math::floor(cpos.x);
+					}
 					cpos += gl.rect.position;
 					cpos += gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
@@ -1842,11 +1933,26 @@ void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
 	MutexLock lock(fd->mutex);
 	MutexLock lock(fd->mutex);
 	Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
 	Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
 	ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
-	if (!_ensure_glyph(fd, size, p_index)) {
+
+	int32_t index = p_index;
+
+#ifdef MODULE_FREETYPE_ENABLED
+	if (!fd->msdf && fd->cache[size]->face) {
+		if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
+			index = index | (xshift << 27);
+		} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+			int xshift = (int)(Math::floor(2 * (p_pos.x + 0.25)) - 2 * Math::floor(p_pos.x + 0.25));
+			index = index | (xshift << 27);
+		}
+	}
+#endif
+
+	if (!_ensure_glyph(fd, size, index)) {
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 		return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
 	}
 	}
 
 
-	const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+	const FontGlyph &gl = fd->cache[size]->glyph_map[index];
 	if (gl.found) {
 	if (gl.found) {
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 		ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
 
 
@@ -1865,7 +1971,15 @@ void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
 					RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
 				} else {
 				} else {
-					Point2 cpos = p_pos.floor();
+					Point2 cpos = p_pos;
+					cpos.y = Math::floor(cpos.y);
+					if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.125));
+					} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+						cpos.x = ((int)Math::floor(cpos.x + 0.25));
+					} else {
+						cpos.x = Math::floor(cpos.x);
+					}
 					cpos += gl.rect.position;
 					cpos += gl.rect.position;
 					Size2 csize = gl.rect.size;
 					Size2 csize = gl.rect.size;
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
 					RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
@@ -2660,7 +2774,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
 			end_pos = sd->overrun_trim_data.trim_pos;
 			end_pos = sd->overrun_trim_data.trim_pos;
 			justification_width = sd->width_trimmed;
 			justification_width = sd->width_trimmed;
 		} else {
 		} else {
-			return sd->width;
+			return Math::ceil(sd->width);
 		}
 		}
 	} else {
 	} else {
 		justification_width = sd->width;
 		justification_width = sd->width;
@@ -2720,7 +2834,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
 		sd->width = justification_width;
 		sd->width = justification_width;
 	}
 	}
 
 
-	return justification_width;
+	return Math::ceil(justification_width);
 }
 }
 
 
 float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
 float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
@@ -3109,6 +3223,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 				}
 				}
 
 
 				if (gl.font_rid.is_valid()) {
 				if (gl.font_rid.is_valid()) {
+					bool subpos = (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_AUTO && gl.font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
 					if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
 					if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
 						if (sd->orientation == ORIENTATION_HORIZONTAL) {
 						if (sd->orientation == ORIENTATION_HORIZONTAL) {
 							gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x);
 							gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x);
@@ -3143,6 +3258,9 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
 							}
 							}
 						}
 						}
 					}
 					}
+					if (sd->orientation == ORIENTATION_HORIZONTAL && !subpos) {
+						gl.advance = Math::round(gl.advance);
+					}
 				} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
 				} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
 					// Glyph not found, replace with hex code box.
 					// Glyph not found, replace with hex code box.
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
 					if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -3312,9 +3430,9 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
 		const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
 		const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
 	}
 	}
 	if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
 	if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
-		return Size2(sd->width, sd->ascent + sd->descent);
+		return Size2(sd->width, sd->ascent + sd->descent).ceil();
 	} else {
 	} else {
-		return Size2(sd->ascent + sd->descent, sd->width);
+		return Size2(sd->ascent + sd->descent, sd->width).ceil();
 	}
 	}
 }
 }
 
 
@@ -3348,7 +3466,7 @@ float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
 	if (!sd->valid) {
 	if (!sd->valid) {
 		const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
 		const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
 	}
 	}
-	return sd->width;
+	return Math::ceil(sd->width);
 }
 }
 
 
 float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
 float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {

+ 4 - 0
modules/text_server_fb/text_server_fb.h

@@ -139,6 +139,7 @@ class TextServerFallback : public TextServer {
 		int fixed_size = 0;
 		int fixed_size = 0;
 		bool force_autohinter = false;
 		bool force_autohinter = false;
 		TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
 		TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+		TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
 		Dictionary variation_coordinates;
 		Dictionary variation_coordinates;
 		float oversampling = 0.f;
 		float oversampling = 0.f;
 
 
@@ -290,6 +291,9 @@ public:
 	virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
 	virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
 	virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
 	virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
 
 
+	virtual void font_set_subpixel_positioning(RID p_font_rid, SubpixelPositioning p_subpixel) override;
+	virtual SubpixelPositioning font_get_subpixel_positioning(RID p_font_rid) const override;
+
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 
 

+ 1 - 1
scene/gui/code_edit.cpp

@@ -84,7 +84,7 @@ void CodeEdit::_notification(int p_what) {
 			if (line_length_guideline_columns.size() > 0) {
 			if (line_length_guideline_columns.size() > 0) {
 				const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
 				const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
 				const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
 				const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
-				const int char_size = Math::round(font->get_char_size('0', 0, font_size).width);
+				const float char_size = font->get_char_size('0', 0, font_size).width;
 
 
 				for (int i = 0; i < line_length_guideline_columns.size(); i++) {
 				for (int i = 0; i < line_length_guideline_columns.size(); i++) {
 					const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
 					const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();

+ 1 - 1
scene/gui/line_edit.cpp

@@ -1606,7 +1606,7 @@ Size2 LineEdit::get_minimum_size() const {
 	Size2 min_size;
 	Size2 min_size;
 
 
 	// Minimum size of text.
 	// Minimum size of text.
-	int em_space_size = font->get_char_size('M', 0, font_size).x;
+	float em_space_size = font->get_char_size('M', 0, font_size).x;
 	min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
 	min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
 
 
 	if (expand_to_text_length) {
 	if (expand_to_text_length) {

+ 1 - 1
scene/gui/text_edit.cpp

@@ -961,7 +961,7 @@ void TextEdit::_notification(int p_what) {
 
 
 						// Give visual indication of empty selected line.
 						// Give visual indication of empty selected line.
 						if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
 						if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
-							int char_w = font->get_char_size(' ', 0, font_size).width;
+							float char_w = font->get_char_size(' ', 0, font_size).width;
 							if (rtl) {
 							if (rtl) {
 								RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color);
 								RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color);
 							} else {
 							} else {

+ 12 - 1
scene/register_scene_types.cpp

@@ -1075,11 +1075,22 @@ void initialize_theme() {
 	// Allow creating the default theme at a different scale to suit higher/lower base resolutions.
 	// Allow creating the default theme at a different scale to suit higher/lower base resolutions.
 	float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0);
 	float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0);
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
 	String theme_path = GLOBAL_DEF_RST("gui/theme/custom", "");
 	String theme_path = GLOBAL_DEF_RST("gui/theme/custom", "");
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
 	String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", "");
 	String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", "");
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
 	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
 
 
+	bool font_antialiased = (bool)GLOBAL_DEF_RST("gui/theme/default_font_antialiased", true);
+	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiased", PropertyInfo(Variant::BOOL, "gui/theme/default_font_antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
+	TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT);
+	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
+	TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO);
+	ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+
 	Ref<Font> font;
 	Ref<Font> font;
 	if (!font_path.is_empty()) {
 	if (!font_path.is_empty()) {
 		font = ResourceLoader::load(font_path);
 		font = ResourceLoader::load(font_path);
@@ -1090,7 +1101,7 @@ void initialize_theme() {
 
 
 	// Always make the default theme to avoid invalid default font/icon/style in the given theme.
 	// Always make the default theme to avoid invalid default font/icon/style in the given theme.
 	if (RenderingServer::get_singleton()) {
 	if (RenderingServer::get_singleton()) {
-		make_default_theme(default_theme_scale, font);
+		make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiased);
 	}
 	}
 
 
 	if (!theme_path.is_empty()) {
 	if (!theme_path.is_empty()) {

+ 4 - 1
scene/resources/default_theme/default_theme.cpp

@@ -1018,7 +1018,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
 	default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2);
 	default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2);
 }
 }
 
 
-void make_default_theme(float p_scale, Ref<Font> p_font) {
+void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa) {
 	Ref<Theme> t;
 	Ref<Theme> t;
 	t.instantiate();
 	t.instantiate();
 
 
@@ -1040,6 +1040,9 @@ void make_default_theme(float p_scale, Ref<Font> p_font) {
 		Ref<FontData> dynamic_font_data;
 		Ref<FontData> dynamic_font_data;
 		dynamic_font_data.instantiate();
 		dynamic_font_data.instantiate();
 		dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
 		dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
+		dynamic_font_data->set_subpixel_positioning(p_subpixel);
+		dynamic_font_data->set_hinting(p_hinting);
+		dynamic_font_data->set_antialiased(p_aa);
 		dynamic_font->add_data(dynamic_font_data);
 		dynamic_font->add_data(dynamic_font_data);
 
 
 		default_font = dynamic_font;
 		default_font = dynamic_font;

+ 1 - 1
scene/resources/default_theme/default_theme.h

@@ -36,7 +36,7 @@
 const int default_font_size = 16;
 const int default_font_size = 16;
 
 
 void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
 void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale);
-void make_default_theme(float p_scale, Ref<Font> p_font);
+void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa);
 void clear_default_theme();
 void clear_default_theme();
 
 
 #endif
 #endif

+ 21 - 0
scene/resources/font.cpp

@@ -60,6 +60,7 @@ _FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const {
 		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_force_autohinter(cache[p_cache_index], force_autohinter);
 		TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter);
 		TS->font_set_hinting(cache[p_cache_index], hinting);
 		TS->font_set_hinting(cache[p_cache_index], hinting);
+		TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning);
 		TS->font_set_oversampling(cache[p_cache_index], oversampling);
 		TS->font_set_oversampling(cache[p_cache_index], oversampling);
 	}
 	}
 }
 }
@@ -101,6 +102,9 @@ void FontData::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
 	ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting);
 	ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
 	ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting);
 
 
+	ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontData::set_subpixel_positioning);
+	ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontData::get_subpixel_positioning);
+
 	ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
 	ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling);
 	ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
 	ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling);
 
 
@@ -204,6 +208,7 @@ void FontData::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
@@ -430,6 +435,7 @@ void FontData::reset_state() {
 	msdf = false;
 	msdf = false;
 	force_autohinter = false;
 	force_autohinter = false;
 	hinting = TextServer::HINTING_LIGHT;
 	hinting = TextServer::HINTING_LIGHT;
+	subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
 	msdf_pixel_range = 14;
 	msdf_pixel_range = 14;
 	msdf_size = 128;
 	msdf_size = 128;
 	fixed_size = 0;
 	fixed_size = 0;
@@ -1364,6 +1370,21 @@ TextServer::Hinting FontData::get_hinting() const {
 	return hinting;
 	return hinting;
 }
 }
 
 
+void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) {
+	if (subpixel_positioning != p_subpixel) {
+		subpixel_positioning = p_subpixel;
+		for (int i = 0; i < cache.size(); i++) {
+			_ensure_rid(i);
+			TS->font_set_subpixel_positioning(cache[i], subpixel_positioning);
+		}
+		emit_changed();
+	}
+}
+
+TextServer::SubpixelPositioning FontData::get_subpixel_positioning() const {
+	return subpixel_positioning;
+}
+
 void FontData::set_oversampling(real_t p_oversampling) {
 void FontData::set_oversampling(real_t p_oversampling) {
 	if (oversampling != p_oversampling) {
 	if (oversampling != p_oversampling) {
 		oversampling = p_oversampling;
 		oversampling = p_oversampling;

+ 4 - 0
scene/resources/font.h

@@ -55,6 +55,7 @@ class FontData : public Resource {
 	int fixed_size = 0;
 	int fixed_size = 0;
 	bool force_autohinter = false;
 	bool force_autohinter = false;
 	TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
 	TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+	TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
 	real_t oversampling = 0.f;
 	real_t oversampling = 0.f;
 
 
 	// Cache.
 	// Cache.
@@ -118,6 +119,9 @@ public:
 	virtual void set_hinting(TextServer::Hinting p_hinting);
 	virtual void set_hinting(TextServer::Hinting p_hinting);
 	virtual TextServer::Hinting get_hinting() const;
 	virtual TextServer::Hinting get_hinting() const;
 
 
+	virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel);
+	virtual TextServer::SubpixelPositioning get_subpixel_positioning() const;
+
 	virtual void set_oversampling(real_t p_oversampling);
 	virtual void set_oversampling(real_t p_oversampling);
 	virtual real_t get_oversampling() const;
 	virtual real_t get_oversampling() const;
 
 

+ 15 - 0
servers/text/text_server_extension.cpp

@@ -85,6 +85,9 @@ void TextServerExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_font_set_hinting, "font_rid", "hinting");
 	GDVIRTUAL_BIND(_font_set_hinting, "font_rid", "hinting");
 	GDVIRTUAL_BIND(_font_get_hinting, "font_rid");
 	GDVIRTUAL_BIND(_font_get_hinting, "font_rid");
 
 
+	GDVIRTUAL_BIND(_font_set_subpixel_positioning, "font_rid", "subpixel_positioning");
+	GDVIRTUAL_BIND(_font_get_subpixel_positioning, "font_rid");
+
 	GDVIRTUAL_BIND(_font_set_variation_coordinates, "font_rid", "variation_coordinates");
 	GDVIRTUAL_BIND(_font_set_variation_coordinates, "font_rid", "variation_coordinates");
 	GDVIRTUAL_BIND(_font_get_variation_coordinates, "font_rid");
 	GDVIRTUAL_BIND(_font_get_variation_coordinates, "font_rid");
 
 
@@ -512,6 +515,18 @@ TextServer::Hinting TextServerExtension::font_get_hinting(RID p_font_rid) const
 	return TextServer::Hinting::HINTING_NONE;
 	return TextServer::Hinting::HINTING_NONE;
 }
 }
 
 
+void TextServerExtension::font_set_subpixel_positioning(RID p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
+	GDVIRTUAL_CALL(_font_set_subpixel_positioning, p_font_rid, p_subpixel);
+}
+
+TextServer::SubpixelPositioning TextServerExtension::font_get_subpixel_positioning(RID p_font_rid) const {
+	TextServer::SubpixelPositioning ret;
+	if (GDVIRTUAL_CALL(_font_get_subpixel_positioning, p_font_rid, ret)) {
+		return (TextServer::SubpixelPositioning)ret;
+	}
+	return TextServer::SubpixelPositioning::SUBPIXEL_POSITIONING_DISABLED;
+}
+
 void TextServerExtension::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 void TextServerExtension::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
 	GDVIRTUAL_CALL(_font_set_variation_coordinates, p_font_rid, p_variation_coordinates);
 	GDVIRTUAL_CALL(_font_set_variation_coordinates, p_font_rid, p_variation_coordinates);
 }
 }

+ 5 - 0
servers/text/text_server_extension.h

@@ -134,6 +134,11 @@ public:
 	GDVIRTUAL2(_font_set_hinting, RID, Hinting);
 	GDVIRTUAL2(_font_set_hinting, RID, Hinting);
 	GDVIRTUAL1RC(Hinting, _font_get_hinting, RID);
 	GDVIRTUAL1RC(Hinting, _font_get_hinting, RID);
 
 
+	virtual void font_set_subpixel_positioning(RID p_font_rid, SubpixelPositioning p_subpixel) override;
+	virtual SubpixelPositioning font_get_subpixel_positioning(RID p_font_rid) const override;
+	GDVIRTUAL2(_font_set_subpixel_positioning, RID, SubpixelPositioning);
+	GDVIRTUAL1RC(SubpixelPositioning, _font_get_subpixel_positioning, RID);
+
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
 	GDVIRTUAL2(_font_set_variation_coordinates, RID, Dictionary);
 	GDVIRTUAL2(_font_set_variation_coordinates, RID, Dictionary);

+ 10 - 1
servers/text_server.cpp

@@ -235,9 +235,12 @@ void TextServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
 	ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter);
 	ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
 	ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter);
 
 
-	ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "_hinting"), &TextServer::font_set_hinting);
+	ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting);
 	ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
 	ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting);
 
 
+	ClassDB::bind_method(D_METHOD("font_set_subpixel_positioning", "font_rid", "subpixel_positioning"), &TextServer::font_set_subpixel_positioning);
+	ClassDB::bind_method(D_METHOD("font_get_subpixel_positioning", "font_rid"), &TextServer::font_get_subpixel_positioning);
+
 	ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates);
 	ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates);
 	ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates);
 	ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates);
 
 
@@ -479,6 +482,12 @@ void TextServer::_bind_methods() {
 	BIND_ENUM_CONSTANT(HINTING_LIGHT);
 	BIND_ENUM_CONSTANT(HINTING_LIGHT);
 	BIND_ENUM_CONSTANT(HINTING_NORMAL);
 	BIND_ENUM_CONSTANT(HINTING_NORMAL);
 
 
+	/* SubpixelPositioning */
+	BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_DISABLED);
+	BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_AUTO);
+	BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF);
+	BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER);
+
 	/* Feature */
 	/* Feature */
 	BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT);
 	BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT);
 	BIND_ENUM_CONSTANT(FEATURE_VERTICAL_LAYOUT);
 	BIND_ENUM_CONSTANT(FEATURE_VERTICAL_LAYOUT);

+ 14 - 0
servers/text_server.h

@@ -101,6 +101,16 @@ public:
 		HINTING_NORMAL
 		HINTING_NORMAL
 	};
 	};
 
 
+	enum SubpixelPositioning {
+		SUBPIXEL_POSITIONING_DISABLED,
+		SUBPIXEL_POSITIONING_AUTO,
+		SUBPIXEL_POSITIONING_ONE_HALF,
+		SUBPIXEL_POSITIONING_ONE_QUARTER,
+	};
+
+	const int SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE = 20;
+	const int SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE = 16;
+
 	enum Feature {
 	enum Feature {
 		FEATURE_BIDI_LAYOUT = 1 << 0,
 		FEATURE_BIDI_LAYOUT = 1 << 0,
 		FEATURE_VERTICAL_LAYOUT = 1 << 1,
 		FEATURE_VERTICAL_LAYOUT = 1 << 1,
@@ -248,6 +258,9 @@ public:
 	virtual void font_set_hinting(RID p_font_rid, Hinting p_hinting) = 0;
 	virtual void font_set_hinting(RID p_font_rid, Hinting p_hinting) = 0;
 	virtual Hinting font_get_hinting(RID p_font_rid) const = 0;
 	virtual Hinting font_get_hinting(RID p_font_rid) const = 0;
 
 
+	virtual void font_set_subpixel_positioning(RID p_font_rid, SubpixelPositioning p_subpixel) = 0;
+	virtual SubpixelPositioning font_get_subpixel_positioning(RID p_font_rid) const = 0;
+
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) = 0;
 	virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) = 0;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const = 0;
 	virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const = 0;
 
 
@@ -551,6 +564,7 @@ VARIANT_ENUM_CAST(TextServer::LineBreakFlag);
 VARIANT_ENUM_CAST(TextServer::TextOverrunFlag);
 VARIANT_ENUM_CAST(TextServer::TextOverrunFlag);
 VARIANT_ENUM_CAST(TextServer::GraphemeFlag);
 VARIANT_ENUM_CAST(TextServer::GraphemeFlag);
 VARIANT_ENUM_CAST(TextServer::Hinting);
 VARIANT_ENUM_CAST(TextServer::Hinting);
+VARIANT_ENUM_CAST(TextServer::SubpixelPositioning);
 VARIANT_ENUM_CAST(TextServer::Feature);
 VARIANT_ENUM_CAST(TextServer::Feature);
 VARIANT_ENUM_CAST(TextServer::ContourPointTag);
 VARIANT_ENUM_CAST(TextServer::ContourPointTag);
 VARIANT_ENUM_CAST(TextServer::SpacingType);
 VARIANT_ENUM_CAST(TextServer::SpacingType);

+ 1 - 1
tests/test_main.cpp

@@ -206,7 +206,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
 			memnew(InputMap);
 			memnew(InputMap);
 			InputMap::get_singleton()->load_default();
 			InputMap::get_singleton()->load_default();
 
 
-			make_default_theme(1.0, Ref<Font>());
+			make_default_theme(1.0, Ref<Font>(), TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::HINTING_LIGHT, true);
 
 
 			memnew(SceneTree);
 			memnew(SceneTree);
 			SceneTree::get_singleton()->initialize();
 			SceneTree::get_singleton()->initialize();