Ported: Fix signed distance field font rendering

This fix works in both GLES3 and GLES2.
The rendering formula in the shader was adjusted to further improve the
sharpness/antialiasing quality balance.
- lawnjelly and Calinou
bc607fb607
This commit is contained in:
Relintai 2024-02-08 19:11:34 +01:00
parent 1a3f34dbf8
commit ae7409bf52
16 changed files with 65 additions and 14 deletions

View File

@ -59,6 +59,7 @@ void RasterizerCanvasBaseGLES2::canvas_begin() {
state.using_large_vertex = false; state.using_large_vertex = false;
state.using_modulate = 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_LIGHT_ANGLE, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false);

View File

@ -1222,6 +1222,7 @@ void RasterizerCanvasGLES2::canvas_render_items_implementation(Item *p_item_list
ris.item_group_modulate = p_modulate; ris.item_group_modulate = p_modulate;
ris.item_group_light = p_light; ris.item_group_light = p_light;
ris.item_group_base_transform = p_base_transform; ris.item_group_base_transform = p_base_transform;
ris.prev_distance_field = false;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_SKELETON, false);
@ -1555,6 +1556,12 @@ bool RasterizerCanvasGLES2::try_join_item(Item *p_ci, RenderItemState &r_ris, bo
join = false; 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 // 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)) { if (!r_batch_break && _detect_item_batch_break(r_ris, p_ci, r_batch_break)) {
join = false; join = false;
@ -1570,6 +1577,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) { void RasterizerCanvasGLES2::_legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris) {
storage->info.render._2d_item_count++; 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) { if (r_ris.current_clip != p_ci->final_clip_owner) {
r_ris.current_clip = p_ci->final_clip_owner; r_ris.current_clip = p_ci->final_clip_owner;
@ -1934,6 +1947,12 @@ void RasterizerCanvasGLES2::render_joined_item(const BItemJoined &p_bij, RenderI
// all the joined items will share the same state with the first item // all the joined items will share the same state with the first item
Item *ci = bdata.item_refs[p_bij.first_item_ref].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) { if (r_ris.current_clip != ci->final_clip_owner) {
r_ris.current_clip = ci->final_clip_owner; r_ris.current_clip = ci->final_clip_owner;

View File

@ -443,10 +443,18 @@ void main() {
uv = mod(uv, vec2(1.0, 1.0)); uv = mod(uv, vec2(1.0, 1.0));
#endif #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) #if !defined(COLOR_USED)
//default behavior, texture by color // Default behavior, texture by color.
color *= texture2D(color_texture, uv); color *= texture2D(color_texture, uv);
#endif #endif
#endif
#ifdef SCREEN_UV_USED #ifdef SCREEN_UV_USED
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;

View File

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

View File

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

View File

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

View File

@ -91,6 +91,7 @@ void Label::_notification(int p_what) {
Size2 size = get_size(); Size2 size = get_size();
Ref<StyleBox> style = get_theme_stylebox("normal"); Ref<StyleBox> style = get_theme_stylebox("normal");
Ref<Font> font = get_theme_font("font"); Ref<Font> font = get_theme_font("font");
select_font(font);
Color font_color = get_theme_color("font_color"); Color font_color = get_theme_color("font_color");
Color font_color_shadow = get_theme_color("font_color_shadow"); Color font_color_shadow = get_theme_color("font_color_shadow");
bool use_outline = get_theme_constant("shadow_as_outline"); bool use_outline = get_theme_constant("shadow_as_outline");
@ -100,8 +101,6 @@ void Label::_notification(int p_what) {
style->draw(ci, Rect2(Point2(0, 0), get_size())); style->draw(ci, Rect2(Point2(0, 0), get_size()));
RenderingServer::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 font_h = font->get_height() + line_spacing;
int lines_visible = (size.y + line_spacing) / font_h; int lines_visible = (size.y + line_spacing) / font_h;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -971,6 +971,16 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid); RenderingServer::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;
RenderingServer::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) { 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."); ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
@ -1359,6 +1369,7 @@ CanvasItem::CanvasItem() :
global_invalid = true; global_invalid = true;
notify_local_transform = false; notify_local_transform = false;
notify_transform = false; notify_transform = false;
font_sdf_selected = false;
light_mask = 1; light_mask = 1;
C = nullptr; C = nullptr;

View File

@ -189,21 +189,22 @@ private:
int light_mask; int light_mask;
bool first_draw; bool first_draw : 1;
bool visible; bool visible : 1;
bool pending_update; bool pending_update : 1;
bool toplevel; bool toplevel : 1;
bool drawing; bool drawing : 1;
bool block_transform_notify; bool block_transform_notify : 1;
bool behind; bool behind : 1;
bool use_parent_material; bool use_parent_material : 1;
bool notify_local_transform; bool notify_local_transform : 1;
bool notify_transform; bool notify_transform : 1;
bool font_sdf_selected : 1;
mutable bool global_invalid : 1;
Ref<Material> material; Ref<Material> material;
mutable Transform2D global_transform; mutable Transform2D global_transform;
mutable bool global_invalid;
void _toplevel_raise_self(); void _toplevel_raise_self();
void _toplevel_visibility_changed(bool p_visible); void _toplevel_visibility_changed(bool p_visible);
@ -332,6 +333,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_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 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); 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)); 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));