Răsfoiți Sursa

Merge pull request #86786 from lawnjelly/calinou_sdf_fixed

[3.x] Fix signed distance field font rendering
Rémi Verschelde 1 an în urmă
părinte
comite
991e922877

+ 1 - 0
drivers/gles2/rasterizer_canvas_base_gles2.cpp

@@ -58,6 +58,7 @@ void RasterizerCanvasBaseGLES2::canvas_begin() {
 	state.using_large_vertex = false;
 	state.using_modulate = false;
 
+	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false);

+ 19 - 0
drivers/gles2/rasterizer_canvas_gles2.cpp

@@ -1225,6 +1225,7 @@ void RasterizerCanvasGLES2::canvas_render_items_implementation(Item *p_item_list
 	ris.item_group_modulate = p_modulate;
 	ris.item_group_light = p_light;
 	ris.item_group_base_transform = p_base_transform;
+	ris.prev_distance_field = false;
 
 	state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, false);
 
@@ -1558,6 +1559,12 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 		join = false;
 	}
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		r_ris.prev_distance_field = p_ci->distance_field;
+		join = false;
+		r_batch_break = true;
+	}
+
 	// non rects will break the batching anyway, we don't want to record item changes, detect this
 	if (!r_batch_break && _detect_item_batch_break(r_ris, p_ci, r_batch_break)) {
 		join = false;
@@ -1573,6 +1580,12 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 void RasterizerCanvasGLES2::_legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris) {
 	storage->info.render._2d_item_count++;
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, p_ci->distance_field);
+		r_ris.prev_distance_field = p_ci->distance_field;
+		r_ris.rebind_shader = true;
+	}
+
 	if (r_ris.current_clip != p_ci->final_clip_owner) {
 		r_ris.current_clip = p_ci->final_clip_owner;
 
@@ -1936,6 +1949,12 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
 	// all the joined items will share the same state with the first item
 	Item *ci = bdata.item_refs[p_bij.first_item_ref].item;
 
+	if (r_ris.prev_distance_field != ci->distance_field) {
+		state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_DISTANCE_FIELD, ci->distance_field);
+		r_ris.prev_distance_field = ci->distance_field;
+		r_ris.rebind_shader = true;
+	}
+
 	if (r_ris.current_clip != ci->final_clip_owner) {
 		r_ris.current_clip = ci->final_clip_owner;
 

+ 9 - 1
drivers/gles2/shaders/canvas.glsl

@@ -443,10 +443,18 @@ void main() {
 	uv = mod(uv, vec2(1.0, 1.0));
 #endif
 
+#ifdef USE_DISTANCE_FIELD
+	// Higher is smoother, but also more blurry. Lower is crisper, but also more aliased.
+	// TODO: Adjust automatically based on screen resolution/font size ratio.
+	const float smoothing = 0.125;
+	float dist = texture2D(color_texture, uv).a;
+	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
+#else
 #if !defined(COLOR_USED)
-	//default behavior, texture by color
+	// Default behavior, texture by color.
 	color *= texture2D(color_texture, uv);
 #endif
+#endif
 
 #ifdef SCREEN_UV_USED
 	vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;

+ 1 - 0
drivers/gles3/rasterizer_canvas_base_gles3.cpp

@@ -165,6 +165,7 @@ void RasterizerCanvasBaseGLES3::canvas_begin() {
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
 
+	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LIGHT_ANGLE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_MODULATE, false);
 	state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_ATTRIB_LARGE_VERTEX, false);

+ 6 - 0
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -1878,6 +1878,12 @@ bool RasterizerCanvasGLES3::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
 		join = false;
 	}
 
+	if (r_ris.prev_distance_field != p_ci->distance_field) {
+		r_ris.prev_distance_field = p_ci->distance_field;
+		join = false;
+		r_batch_break = true;
+	}
+
 	// non rects will break the batching anyway, we don't want to record item changes, detect this
 	if (!r_batch_break && _detect_item_batch_break(r_ris, p_ci, r_batch_break)) {
 		join = false;

+ 7 - 8
drivers/gles3/shaders/canvas.glsl

@@ -581,18 +581,17 @@ void main() {
 
 #endif
 
-#if !defined(COLOR_USED)
-	//default behavior, texture by color
-
 #ifdef USE_DISTANCE_FIELD
-	const float smoothing = 1.0 / 32.0;
-	float distance = textureLod(color_texture, uv, 0.0).a;
-	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance) * color.a;
+	// Higher is smoother, but also more blurry. Lower is crisper, but also more aliased.
+	// TODO: Adjust automatically based on screen resolution/font size ratio.
+	const float smoothing = 0.125;
+	float dist = texture(color_texture, uv, 0.0).a;
+	color.a = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
 #else
+#if !defined(COLOR_USED)
+	// Default behavior, texture by color.
 	color *= texture(color_texture, uv);
-
 #endif
-
 #endif
 
 	vec3 normal;

+ 12 - 0
scene/2d/canvas_item.cpp

@@ -955,6 +955,17 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
 	VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid);
 }
 
+void CanvasItem::select_font(const Ref<Font> &p_font) {
+	// Purely to keep canvas item SDF state up to date for now.
+	bool new_font_sdf_selected = p_font.is_valid() && p_font->is_distance_field_hint();
+
+	if (font_sdf_selected != new_font_sdf_selected) {
+		ERR_FAIL_COND(!get_canvas_item().is_valid());
+		font_sdf_selected = new_font_sdf_selected;
+		VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(), font_sdf_selected);
+	}
+}
+
 void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) {
 	ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
 
@@ -1351,6 +1362,7 @@ CanvasItem::CanvasItem() :
 	global_invalid = true;
 	notify_local_transform = false;
 	notify_transform = false;
+	font_sdf_selected = false;
 	light_mask = 1;
 
 	C = nullptr;

+ 13 - 11
scene/2d/canvas_item.h

@@ -191,21 +191,22 @@ private:
 
 	int light_mask;
 
-	bool first_draw;
-	bool visible;
-	bool pending_update;
-	bool toplevel;
-	bool drawing;
-	bool block_transform_notify;
-	bool behind;
-	bool use_parent_material;
-	bool notify_local_transform;
-	bool notify_transform;
+	bool first_draw : 1;
+	bool visible : 1;
+	bool pending_update : 1;
+	bool toplevel : 1;
+	bool drawing : 1;
+	bool block_transform_notify : 1;
+	bool behind : 1;
+	bool use_parent_material : 1;
+	bool notify_local_transform : 1;
+	bool notify_transform : 1;
+	bool font_sdf_selected : 1;
+	mutable bool global_invalid : 1;
 
 	Ref<Material> material;
 
 	mutable Transform2D global_transform;
-	mutable bool global_invalid;
 
 	void _toplevel_raise_self();
 	void _toplevel_visibility_changed(bool p_visible);
@@ -335,6 +336,7 @@ public:
 	void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
 	void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
 
+	void select_font(const Ref<Font> &p_font);
 	void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);
 	float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1));
 

+ 1 - 0
scene/gui/button.cpp

@@ -286,6 +286,7 @@ void Button::_notification(int p_what) {
 
 				text_ofs.y += font->get_ascent();
 				text_ofs.y += line_height * (((float)i) - (((float)(num_lines - 1)) / 2.0));
+				select_font(font);
 				font->draw(ci, text_ofs.floor(), line_text, color, clip_text ? text_clip : -1);
 			}
 		} break;

+ 1 - 0
scene/gui/dialogs.cpp

@@ -206,6 +206,7 @@ void WindowDialog::_notification(int p_what) {
 			int font_height = title_font->get_height() - title_font->get_descent() * 2;
 			int x = (size.x - title_font->get_string_size(xl_title).x) / 2;
 			int y = (-title_height + font_height) / 2;
+			select_font(title_font);
 			title_font->draw(canvas, Point2(x, y), xl_title, title_color, size.x - panel->get_minimum_size().x);
 		} break;
 

+ 1 - 0
scene/gui/item_list.cpp

@@ -767,6 +767,7 @@ void ItemList::_notification(int p_what) {
 		Ref<StyleBox> cursor = has_focus() ? get_stylebox("cursor") : get_stylebox("cursor_unfocused");
 
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color guide_color = get_color("guide_color");
 		Color font_color = get_color("font_color");
 		Color font_color_selected = get_color("font_color_selected");

+ 1 - 2
scene/gui/label.cpp

@@ -90,6 +90,7 @@ void Label::_notification(int p_what) {
 		Size2 size = get_size();
 		Ref<StyleBox> style = get_stylebox("normal");
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color font_color = get_color("font_color");
 		Color font_color_shadow = get_color("font_color_shadow");
 		bool use_outline = get_constant("shadow_as_outline");
@@ -99,8 +100,6 @@ void Label::_notification(int p_what) {
 
 		style->draw(ci, Rect2(Point2(0, 0), get_size()));
 
-		VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(), font.is_valid() && font->is_distance_field_hint());
-
 		int font_h = font->get_height() + line_spacing;
 
 		int lines_visible = (size.y + line_spacing) / font_h;

+ 1 - 0
scene/gui/line_edit.cpp

@@ -813,6 +813,7 @@ void LineEdit::_notification(int p_what) {
 			}
 
 			Ref<Font> font = get_font("font");
+			select_font(font);
 
 			style->draw(ci, Rect2(Point2(), size));
 

+ 1 - 0
scene/gui/link_button.cpp

@@ -128,6 +128,7 @@ void LinkButton::_notification(int p_what) {
 			}
 
 			Ref<Font> font = get_font("font");
+			select_font(font);
 
 			draw_string(font, Vector2(0, font->get_ascent()), xl_text, color);
 

+ 2 - 0
scene/gui/popup_menu.cpp

@@ -466,6 +466,8 @@ void PopupMenu::_draw_items() {
 	Ref<StyleBox> style = get_stylebox("panel");
 	Ref<StyleBox> hover = get_stylebox("hover");
 	Ref<Font> font = get_font("font");
+	select_font(font);
+
 	// In Item::checkable_type enum order (less the non-checkable member)
 	Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") };
 	Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };

+ 1 - 0
scene/gui/progress_bar.cpp

@@ -52,6 +52,7 @@ void ProgressBar::_notification(int p_what) {
 		Ref<StyleBox> bg = get_stylebox("bg");
 		Ref<StyleBox> fg = get_stylebox("fg");
 		Ref<Font> font = get_font("font");
+		select_font(font);
 		Color font_color = get_color("font_color");
 
 		draw_style_box(bg, Rect2(Point2(), get_size()));

+ 1 - 0
scene/gui/rich_text_label.cpp

@@ -1077,6 +1077,7 @@ void RichTextLabel::_notification(int p_what) {
 			}
 			int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
 			Ref<Font> base_font = get_font("normal_font");
+			select_font(base_font);
 			Color base_color = get_color("default_color");
 			Color font_color_shadow = get_color("font_color_shadow");
 			bool use_outline = get_constant("shadow_as_outline");

+ 1 - 0
scene/gui/tab_container.cpp

@@ -279,6 +279,7 @@ void TabContainer::_notification(int p_what) {
 			Ref<Texture> menu = get_icon("menu");
 			Ref<Texture> menu_hl = get_icon("menu_highlight");
 			Ref<Font> font = get_font("font");
+			select_font(font);
 			Color font_color_fg = get_color("font_color_fg");
 			Color font_color_bg = get_color("font_color_bg");
 			Color font_color_disabled = get_color("font_color_disabled");

+ 1 - 0
scene/gui/tabs.cpp

@@ -243,6 +243,7 @@ void Tabs::_notification(int p_what) {
 			Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
 			Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
 			Ref<Font> font = get_font("font");
+			select_font(font);
 			Color color_fg = get_color("font_color_fg");
 			Color color_bg = get_color("font_color_bg");
 			Color color_disabled = get_color("font_color_disabled");