Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TextServer] Improve empty glyph handling to allow glyphs smaller than 2px and avoid unnecessary texture updates. #90349

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 52 additions & 42 deletions modules/text_server_adv/text_server_adv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ void TextServerAdvanced::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
}
}

_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const {
msdfgen::Shape shape;

shape.contours.clear();
Expand All @@ -974,13 +974,13 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
ft_functions.shift = 0;
ft_functions.delta = 0;

int error = FT_Outline_Decompose(outline, &ft_functions, &context);
int error = FT_Outline_Decompose(p_outline, &ft_functions, &context);
ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
shape.contours.pop_back();
}

if (FT_Outline_Get_Orientation(outline) == 1) {
if (FT_Outline_Get_Orientation(p_outline) == 1) {
for (int i = 0; i < (int)shape.contours.size(); ++i) {
shape.contours[i].reverse();
}
Expand All @@ -993,12 +993,19 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(

FontGlyph chr;
chr.found = true;
chr.advance = advance;
chr.advance = p_advance;

if (shape.validate() && shape.contours.size() > 0) {
int w = (bounds.r - bounds.l);
int h = (bounds.t - bounds.b);

if (w == 0 || h == 0) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
return chr;
}

int mw = w + p_rect_margin * 4;
int mh = h + p_rect_margin * 4;

Expand Down Expand Up @@ -1056,12 +1063,24 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
#endif

#ifdef MODULE_FREETYPE_ENABLED
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const {
int w = bitmap.width;
int h = bitmap.rows;
_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const {
FontGlyph chr;
chr.advance = p_advance * p_data->scale / p_data->oversampling;
chr.found = true;

int w = p_bitmap.width;
int h = p_bitmap.rows;

if (w == 0 || h == 0) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
return chr;
}

int color_size = 2;

switch (bitmap.pixel_mode) {
switch (p_bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
case FT_PIXEL_MODE_GRAY: {
color_size = 2;
Expand Down Expand Up @@ -1100,54 +1119,54 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
for (int j = 0; j < w; j++) {
int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;
ERR_FAIL_COND_V(ofs >= tex.image->data_size(), FontGlyph());
switch (bitmap.pixel_mode) {
switch (p_bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO: {
int byte = i * bitmap.pitch + (j >> 3);
int byte = i * p_bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j];
break;
case FT_PIXEL_MODE_BGRA: {
int ofs_color = i * bitmap.pitch + (j << 2);
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
int ofs_color = i * p_bitmap.pitch + (j << 2);
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3];
} break;
case FT_PIXEL_MODE_LCD: {
int ofs_color = i * bitmap.pitch + (j * 3);
int ofs_color = i * p_bitmap.pitch + (j * 3);
if (p_bgra) {
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 3] = 255;
} else {
wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = bitmap.buffer[ofs_color + 2];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = 255;
}
} break;
case FT_PIXEL_MODE_LCD_V: {
int ofs_color = i * bitmap.pitch * 3 + j;
int ofs_color = i * p_bitmap.pitch * 3 + j;
if (p_bgra) {
wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 3] = 255;
} else {
wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];
wr[ofs + 3] = 255;
}
} break;
default:
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + ".");
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + ".");
break;
}
}
Expand All @@ -1156,13 +1175,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma

tex.dirty = true;

FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
chr.found = true;

chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
chr.rect.position = Vector2(xofs - p_rect_margin, -yofs - p_rect_margin) * p_data->scale / p_data->oversampling;
chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale / p_data->oversampling;
chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
return chr;
}
Expand Down Expand Up @@ -3619,9 +3635,6 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca

const FontGlyph &gl = fd->cache[size]->glyph_map[index];
if (gl.found) {
if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) {
return; // Nothing to draw.
}
ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());

if (gl.texture_idx != -1) {
Expand Down Expand Up @@ -3730,9 +3743,6 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R

const FontGlyph &gl = fd->cache[size]->glyph_map[index];
if (gl.found) {
if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) {
return; // Nothing to draw.
}
ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());

if (gl.texture_idx != -1) {
Expand Down
4 changes: 2 additions & 2 deletions modules/text_server_adv/text_server_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ class TextServerAdvanced : public TextServerExtension {

_FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
#ifdef MODULE_MSDFGEN_ENABLED
_FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
_FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
_FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const;
_FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const;
#endif
_FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
_FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const;
Expand Down
93 changes: 51 additions & 42 deletions modules/text_server_fb/text_server_fb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
}
}

_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *p_outline, const Vector2 &p_advance) const {
msdfgen::Shape shape;

shape.contours.clear();
Expand All @@ -410,13 +410,13 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
ft_functions.shift = 0;
ft_functions.delta = 0;

int error = FT_Outline_Decompose(outline, &ft_functions, &context);
int error = FT_Outline_Decompose(p_outline, &ft_functions, &context);
ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
shape.contours.pop_back();
}

if (FT_Outline_Get_Orientation(outline) == 1) {
if (FT_Outline_Get_Orientation(p_outline) == 1) {
for (int i = 0; i < (int)shape.contours.size(); ++i) {
shape.contours[i].reverse();
}
Expand All @@ -429,12 +429,18 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(

FontGlyph chr;
chr.found = true;
chr.advance = advance;
chr.advance = p_advance;

if (shape.validate() && shape.contours.size() > 0) {
int w = (bounds.r - bounds.l);
int h = (bounds.t - bounds.b);

if (w == 0 || h == 0) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
return chr;
}
int mw = w + p_rect_margin * 4;
int mh = h + p_rect_margin * 4;

Expand Down Expand Up @@ -491,12 +497,24 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
#endif

#ifdef MODULE_FREETYPE_ENABLED
_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const {
int w = bitmap.width;
int h = bitmap.rows;
_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const {
FontGlyph chr;
chr.advance = p_advance * p_data->scale / p_data->oversampling;
chr.found = true;

int w = p_bitmap.width;
int h = p_bitmap.rows;

if (w == 0 || h == 0) {
chr.texture_idx = -1;
chr.uv_rect = Rect2();
chr.rect = Rect2();
return chr;
}

int color_size = 2;

switch (bitmap.pixel_mode) {
switch (p_bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO:
case FT_PIXEL_MODE_GRAY: {
color_size = 2;
Expand Down Expand Up @@ -535,54 +553,54 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
for (int j = 0; j < w; j++) {
int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size;
ERR_FAIL_COND_V(ofs >= tex.image->data_size(), FontGlyph());
switch (bitmap.pixel_mode) {
switch (p_bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO: {
int byte = i * bitmap.pitch + (j >> 3);
int byte = i * p_bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
wr[ofs + 1] = (p_bitmap.buffer[byte] & bit) ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
wr[ofs + 1] = p_bitmap.buffer[i * p_bitmap.pitch + j];
break;
case FT_PIXEL_MODE_BGRA: {
int ofs_color = i * bitmap.pitch + (j << 2);
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
int ofs_color = i * p_bitmap.pitch + (j << 2);
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = p_bitmap.buffer[ofs_color + 3];
} break;
case FT_PIXEL_MODE_LCD: {
int ofs_color = i * bitmap.pitch + (j * 3);
int ofs_color = i * p_bitmap.pitch + (j * 3);
if (p_bgra) {
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 3] = 255;
} else {
wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = bitmap.buffer[ofs_color + 2];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + 1];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = 255;
}
} break;
case FT_PIXEL_MODE_LCD_V: {
int ofs_color = i * bitmap.pitch * 3 + j;
int ofs_color = i * p_bitmap.pitch * 3 + j;
if (p_bgra) {
wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 3] = 255;
} else {
wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
wr[ofs + 0] = p_bitmap.buffer[ofs_color + 0];
wr[ofs + 1] = p_bitmap.buffer[ofs_color + p_bitmap.pitch];
wr[ofs + 2] = p_bitmap.buffer[ofs_color + p_bitmap.pitch * 2];
wr[ofs + 3] = 255;
}
} break;
default:
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + ".");
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(p_bitmap.pixel_mode) + ".");
break;
}
}
Expand All @@ -591,13 +609,10 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma

tex.dirty = true;

FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
chr.found = true;

chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
chr.rect.position = Vector2(xofs - p_rect_margin, -yofs - p_rect_margin) * p_data->scale / p_data->oversampling;
chr.rect.position = Vector2(p_xofs - p_rect_margin, -p_yofs - p_rect_margin) * p_data->scale / p_data->oversampling;
chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
return chr;
}
Expand Down Expand Up @@ -2567,9 +2582,6 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca

const FontGlyph &gl = fd->cache[size]->glyph_map[index];
if (gl.found) {
if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) {
return; // Nothing to draw.
}
ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());

if (gl.texture_idx != -1) {
Expand Down Expand Up @@ -2678,9 +2690,6 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R

const FontGlyph &gl = fd->cache[size]->glyph_map[index];
if (gl.found) {
if (gl.uv_rect.size.x <= 2 || gl.uv_rect.size.y <= 2) {
return; // Nothing to draw.
}
ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());

if (gl.texture_idx != -1) {
Expand Down
Loading
Loading