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. Co-authored-by: Hugo Locurcio <hugo.locurcio@hugo.pro>
This commit is contained in:
parent
a81d96c637
commit
bc607fb607
19 changed files with 80 additions and 22 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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") };
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue