1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-15 13:51:40 +00:00

Improve font glyph cache packing shelf best height fit heuristic.

This commit is contained in:
bruvzg
2022-10-18 10:10:24 +03:00
parent 4a96fce801
commit c1af9d57a3
6 changed files with 246 additions and 185 deletions

View File

@@ -262,7 +262,7 @@
<param index="1" name="size" type="Vector2i" /> <param index="1" name="size" type="Vector2i" />
<param index="2" name="texture_index" type="int" /> <param index="2" name="texture_index" type="int" />
<description> <description>
Returns a copy of the array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty. Returns a copy of the array containing glyph packing data.
</description> </description>
</method> </method>
<method name="get_transform" qualifiers="const"> <method name="get_transform" qualifiers="const">
@@ -522,7 +522,7 @@
<param index="2" name="texture_index" type="int" /> <param index="2" name="texture_index" type="int" />
<param index="3" name="offset" type="PackedInt32Array" /> <param index="3" name="offset" type="PackedInt32Array" />
<description> <description>
Sets array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty (for the fonts without dynamic glyph generation support). Sets array containing glyph packing data.
</description> </description>
</method> </method>
<method name="set_transform"> <method name="set_transform">

View File

@@ -413,7 +413,7 @@
<param index="1" name="size" type="Vector2i" /> <param index="1" name="size" type="Vector2i" />
<param index="2" name="texture_index" type="int" /> <param index="2" name="texture_index" type="int" />
<description> <description>
Returns array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty. Returns array containing glyph packing data.
</description> </description>
</method> </method>
<method name="font_get_transform" qualifiers="const"> <method name="font_get_transform" qualifiers="const">
@@ -824,7 +824,7 @@
<param index="2" name="texture_index" type="int" /> <param index="2" name="texture_index" type="int" />
<param index="3" name="offset" type="PackedInt32Array" /> <param index="3" name="offset" type="PackedInt32Array" />
<description> <description>
Sets array containing the first free pixel in the each column of texture. Should be the same size as texture width or empty. Sets array containing glyph packing data.
</description> </description>
</method> </method>
<method name="font_set_transform"> <method name="font_set_transform">

View File

@@ -788,58 +788,27 @@ String TextServerAdvanced::_tag_to_name(int64_t p_tag) const {
_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::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 { _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::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 {
FontTexturePosition ret; FontTexturePosition ret;
ret.index = -1;
int mw = p_width; int mw = p_width;
int mh = p_height; int mh = p_height;
for (int i = 0; i < p_data->textures.size(); i++) { ShelfPackTexture *ct = p_data->textures.ptrw();
const FontTexture &ct = p_data->textures[i]; for (int32_t i = 0; i < p_data->textures.size(); i++) {
if (p_image_format != ct[i].format) {
if (p_image_format != ct.format) { continue;
}
if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.
continue; continue;
} }
if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. ret = ct[i].pack_rect(i, mh, mw);
continue; if (ret.index != -1) {
break;
} }
if (ct.offsets.size() < ct.texture_w) {
continue;
}
ret.y = 0x7fffffff;
ret.x = 0;
const int *ct_offsets_ptr = ct.offsets.ptr();
for (int j = 0; j < ct.texture_w - mw; j++) {
int max_y = 0;
for (int k = j; k < j + mw; k++) {
int y = ct_offsets_ptr[k];
if (y > max_y) {
max_y = y;
}
}
if (max_y < ret.y) {
ret.y = max_y;
ret.x = j;
}
}
if (ret.y == 0x7fffffff || ret.y + mh > ct.texture_h) {
continue; // Fail, could not fit it here.
}
ret.index = i;
break;
} }
if (ret.index == -1) { if (ret.index == -1) {
// Could not find texture to fit, create one. // Could not find texture to fit, create one.
ret.x = 0;
ret.y = 0;
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
#ifdef GDEXTENSION #ifdef GDEXTENSION
@@ -867,12 +836,9 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
#endif #endif
} }
FontTexture tex; ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
tex.texture_w = texsize;
tex.texture_h = texsize;
tex.format = p_image_format; tex.format = p_image_format;
tex.imgdata.resize(texsize * texsize * p_color_size); tex.imgdata.resize(texsize * texsize * p_color_size);
{ {
// Zero texture. // Zero texture.
uint8_t *w = tex.imgdata.ptrw(); uint8_t *w = tex.imgdata.ptrw();
@@ -895,14 +861,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
ERR_FAIL_V(ret); ERR_FAIL_V(ret);
} }
} }
tex.offsets.resize(texsize);
int32_t *offw = tex.offsets.ptrw();
for (int i = 0; i < texsize; i++) { // Zero offsets.
offw[i] = 0;
}
p_data->textures.push_back(tex); p_data->textures.push_back(tex);
ret.index = p_data->textures.size() - 1;
int32_t idx = p_data->textures.size() - 1;
ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);
} }
return ret; return ret;
@@ -1036,7 +998,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true); FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
FontTexture &tex = p_data->textures.write[tex_pos.index]; ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
edgeColoringSimple(shape, 3.0); // Max. angle. edgeColoringSimple(shape, 3.0); // Max. angle.
msdfgen::Bitmap<float, 4> image(w, h); // Texture size. msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
@@ -1079,12 +1041,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
tex.dirty = true; tex.dirty = true;
// Update height array.
int32_t *offw = tex.offsets.ptrw();
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
offw[k] = tex_pos.y + mh;
}
chr.texture_idx = tex_pos.index; chr.texture_idx = tex_pos.index;
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.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);
@@ -1132,8 +1088,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
// Fit character in char texture. // Fit character in char texture.
ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
FontTexture &tex = p_data->textures.write[tex_pos.index];
{ {
uint8_t *wr = tex.imgdata.ptrw(); uint8_t *wr = tex.imgdata.ptrw();
@@ -1198,12 +1153,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
tex.dirty = true; tex.dirty = true;
// Update height array.
int32_t *offw = tex.offsets.ptrw();
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
offw[k] = tex_pos.y + mh;
}
FontGlyph chr; FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling; chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index; chr.texture_idx = tex_pos.index;
@@ -2492,7 +2441,7 @@ void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Ve
fd->cache[size]->textures.resize(p_texture_index + 1); fd->cache[size]->textures.resize(p_texture_index + 1);
} }
FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.imgdata = p_image->get_data(); tex.imgdata = p_image->get_data();
tex.texture_w = p_image->get_width(); tex.texture_w = p_image->get_width();
@@ -2517,11 +2466,12 @@ Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, co
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
} }
void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {
ERR_FAIL_COND(p_offsets.size() % 4 != 0);
FontAdvanced *fd = font_owner.get_or_null(p_font_rid); FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd); ERR_FAIL_COND(!fd);
@@ -2533,8 +2483,11 @@ void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const
fd->cache[size]->textures.resize(p_texture_index + 1); fd->cache[size]->textures.resize(p_texture_index + 1);
} }
FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.offsets = p_offset; tex.shelves.clear();
for (int32_t i = 0; i < p_offsets.size(); i += 4) {
tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));
}
} }
PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
@@ -2546,8 +2499,20 @@ PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
return tex.offsets; PackedInt32Array ret;
ret.resize(tex.shelves.size() * 4);
int32_t *wr = ret.ptrw();
int32_t i = 0;
for (const Shelf &E : tex.shelves) {
wr[i * 4] = E.x;
wr[i * 4 + 1] = E.y;
wr[i * 4 + 2] = E.w;
wr[i * 4 + 3] = E.h;
i++;
}
return ret;
} }
PackedInt32Array TextServerAdvanced::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const { PackedInt32Array TextServerAdvanced::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
@@ -2851,7 +2816,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (gl[p_glyph | mod].texture_idx != -1) { if (gl[p_glyph | mod].texture_idx != -1) {
if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -2897,7 +2862,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (gl[p_glyph | mod].texture_idx != -1) { if (gl[p_glyph | mod].texture_idx != -1) {
if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -3242,7 +3207,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
#endif #endif
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) { if (fd->cache[size]->textures[gl.texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -3332,7 +3297,7 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R
#endif #endif
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) { if (fd->cache[size]->textures[gl.texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();

View File

@@ -168,20 +168,86 @@ class TextServerAdvanced : public TextServerExtension {
const int rect_range = 1; const int rect_range = 1;
struct FontTexture { struct FontTexturePosition {
Image::Format format; int32_t index = -1;
PackedByteArray imgdata; int32_t x = 0;
int texture_w = 0; int32_t y = 0;
int texture_h = 0;
PackedInt32Array offsets; FontTexturePosition() {}
Ref<ImageTexture> texture; FontTexturePosition(int32_t p_id, int32_t p_x, int32_t p_y) :
bool dirty = true; index(p_id), x(p_x), y(p_y) {}
}; };
struct FontTexturePosition { struct Shelf {
int index = 0; int32_t x = 0;
int x = 0; int32_t y = 0;
int y = 0; int32_t w = 0;
int32_t h = 0;
FontTexturePosition alloc_shelf(int32_t p_id, int32_t p_w, int32_t p_h) {
if (p_w > w || p_h > h) {
return FontTexturePosition(-1, 0, 0);
}
int32_t xx = x;
x += p_w;
w -= p_w;
return FontTexturePosition(p_id, xx, y);
}
Shelf() {}
Shelf(int32_t p_x, int32_t p_y, int32_t p_w, int32_t p_h) :
x(p_x), y(p_y), w(p_w), h(p_h) {}
};
struct ShelfPackTexture {
int32_t texture_w = 1024;
int32_t texture_h = 1024;
Image::Format format;
PackedByteArray imgdata;
Ref<ImageTexture> texture;
bool dirty = true;
List<Shelf> shelves;
FontTexturePosition pack_rect(int32_t p_id, int32_t p_h, int32_t p_w) {
int32_t y = 0;
int32_t waste = 0;
Shelf *best_shelf = nullptr;
int32_t best_waste = std::numeric_limits<std::int32_t>::max();
for (Shelf &E : shelves) {
y += E.h;
if (p_w > E.w) {
continue;
}
if (p_h == E.h) {
return E.alloc_shelf(p_id, p_w, p_h);
}
if (p_h > E.h) {
continue;
}
if (p_h < E.h) {
waste = (E.h - p_h) * p_w;
if (waste < best_waste) {
best_waste = waste;
best_shelf = &E;
}
}
}
if (best_shelf) {
return best_shelf->alloc_shelf(p_id, p_w, p_h);
}
if (p_h <= (texture_h - y) && p_w <= texture_w) {
List<Shelf>::Element *E = shelves.push_back(Shelf(0, y, texture_w, p_h));
return E->get().alloc_shelf(p_id, p_w, p_h);
}
return FontTexturePosition(-1, 0, 0);
}
ShelfPackTexture() {}
ShelfPackTexture(int32_t p_w, int32_t p_h) :
texture_w(p_w), texture_h(p_h) {}
}; };
struct FontGlyph { struct FontGlyph {
@@ -202,7 +268,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector2i size; Vector2i size;
Vector<FontTexture> textures; Vector<ShelfPackTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map; HashMap<int32_t, FontGlyph> glyph_map;
HashMap<Vector2i, Vector2> kerning_map; HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr; hb_font_t *hb_handle = nullptr;

View File

@@ -211,59 +211,27 @@ String TextServerFallback::_tag_to_name(int64_t p_tag) const {
_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const { _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
FontTexturePosition ret; FontTexturePosition ret;
ret.index = -1;
int mw = p_width; int mw = p_width;
int mh = p_height; int mh = p_height;
for (int i = 0; i < p_data->textures.size(); i++) { ShelfPackTexture *ct = p_data->textures.ptrw();
const FontTexture &ct = p_data->textures[i]; for (int32_t i = 0; i < p_data->textures.size(); i++) {
if (p_image_format != ct[i].format) {
if (p_image_format != ct.format) { continue;
}
if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.
continue; continue;
} }
if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture. ret = ct[i].pack_rect(i, mh, mw);
continue; if (ret.index != -1) {
break;
} }
if (ct.offsets.size() < ct.texture_w) {
continue;
}
ret.y = 0x7fffffff;
ret.x = 0;
const int *ct_offsets_ptr = ct.offsets.ptr();
for (int j = 0; j < ct.texture_w - mw; j++) {
int max_y = 0;
for (int k = j; k < j + mw; k++) {
int y = ct_offsets_ptr[k];
if (y > max_y) {
max_y = y;
}
}
if (max_y < ret.y) {
ret.y = max_y;
ret.x = j;
}
}
if (ret.y == 0x7fffffff || ret.y + mh > ct.texture_h) {
continue; // Fail, could not fit it here.
}
ret.index = i;
break;
} }
if (ret.index == -1) { if (ret.index == -1) {
// Could not find texture to fit, create one. // Could not find texture to fit, create one.
ret.x = 0;
ret.y = 0;
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256); int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
#ifdef GDEXTENSION #ifdef GDEXTENSION
@@ -292,12 +260,9 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
#endif #endif
} }
FontTexture tex; ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
tex.texture_w = texsize;
tex.texture_h = texsize;
tex.format = p_image_format; tex.format = p_image_format;
tex.imgdata.resize(texsize * texsize * p_color_size); tex.imgdata.resize(texsize * texsize * p_color_size);
{ {
// Zero texture. // Zero texture.
uint8_t *w = tex.imgdata.ptrw(); uint8_t *w = tex.imgdata.ptrw();
@@ -320,14 +285,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
ERR_FAIL_V(ret); ERR_FAIL_V(ret);
} }
} }
tex.offsets.resize(texsize);
int32_t *offw = tex.offsets.ptrw();
for (int i = 0; i < texsize; i++) { // Zero offsets.
offw[i] = 0;
}
p_data->textures.push_back(tex); p_data->textures.push_back(tex);
ret.index = p_data->textures.size() - 1;
int32_t idx = p_data->textures.size() - 1;
ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);
} }
return ret; return ret;
@@ -461,7 +422,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true); FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
FontTexture &tex = p_data->textures.write[tex_pos.index]; ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
edgeColoringSimple(shape, 3.0); // Max. angle. edgeColoringSimple(shape, 3.0); // Max. angle.
msdfgen::Bitmap<float, 4> image(w, h); // Texture size. msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
@@ -504,12 +465,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
tex.dirty = true; tex.dirty = true;
// Update height array.
int32_t *offw = tex.offsets.ptrw();
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
offw[k] = tex_pos.y + mh;
}
chr.texture_idx = tex_pos.index; chr.texture_idx = tex_pos.index;
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.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);
@@ -556,8 +511,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
// Fit character in char texture. // Fit character in char texture.
ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
FontTexture &tex = p_data->textures.write[tex_pos.index];
{ {
uint8_t *wr = tex.imgdata.ptrw(); uint8_t *wr = tex.imgdata.ptrw();
@@ -622,12 +576,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
tex.dirty = true; tex.dirty = true;
// Update height array.
int32_t *offw = tex.offsets.ptrw();
for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
offw[k] = tex_pos.y + mh;
}
FontGlyph chr; FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling; chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index; chr.texture_idx = tex_pos.index;
@@ -1587,7 +1535,7 @@ void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Ve
fd->cache[size]->textures.resize(p_texture_index + 1); fd->cache[size]->textures.resize(p_texture_index + 1);
} }
FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.imgdata = p_image->get_data(); tex.imgdata = p_image->get_data();
tex.texture_w = p_image->get_width(); tex.texture_w = p_image->get_width();
@@ -1612,11 +1560,12 @@ Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, co
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>()); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
} }
void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {
ERR_FAIL_COND(p_offsets.size() % 4 != 0);
FontFallback *fd = font_owner.get_or_null(p_font_rid); FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd); ERR_FAIL_COND(!fd);
@@ -1628,8 +1577,11 @@ void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const
fd->cache[size]->textures.resize(p_texture_index + 1); fd->cache[size]->textures.resize(p_texture_index + 1);
} }
FontTexture &tex = fd->cache[size]->textures.write[p_texture_index]; ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.offsets = p_offset; tex.shelves.clear();
for (int32_t i = 0; i < p_offsets.size(); i += 4) {
tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));
}
} }
PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const { PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
@@ -1641,8 +1593,20 @@ PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array()); ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array()); ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
return tex.offsets; PackedInt32Array ret;
ret.resize(tex.shelves.size() * 4);
int32_t *wr = ret.ptrw();
int32_t i = 0;
for (const Shelf &E : tex.shelves) {
wr[i * 4] = E.x;
wr[i * 4 + 1] = E.y;
wr[i * 4 + 2] = E.w;
wr[i * 4 + 3] = E.h;
i++;
}
return ret;
} }
PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const { PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
@@ -1932,7 +1896,7 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (gl[p_glyph | mod].texture_idx != -1) { if (gl[p_glyph | mod].texture_idx != -1) {
if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -1978,7 +1942,7 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (gl[p_glyph | mod].texture_idx != -1) { if (gl[p_glyph | mod].texture_idx != -1) {
if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -2305,7 +2269,7 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
#endif #endif
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) { if (fd->cache[size]->textures[gl.texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();
@@ -2395,7 +2359,7 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R
#endif #endif
if (RenderingServer::get_singleton() != nullptr) { if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) { if (fd->cache[size]->textures[gl.texture_idx].dirty) {
FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) { if (fd->mipmaps) {
img->generate_mipmaps(); img->generate_mipmaps();

View File

@@ -127,20 +127,86 @@ class TextServerFallback : public TextServerExtension {
const int rect_range = 1; const int rect_range = 1;
struct FontTexture { struct FontTexturePosition {
Image::Format format; int32_t index = -1;
PackedByteArray imgdata; int32_t x = 0;
int texture_w = 0; int32_t y = 0;
int texture_h = 0;
PackedInt32Array offsets; FontTexturePosition() {}
Ref<ImageTexture> texture; FontTexturePosition(int32_t p_id, int32_t p_x, int32_t p_y) :
bool dirty = true; index(p_id), x(p_x), y(p_y) {}
}; };
struct FontTexturePosition { struct Shelf {
int index = 0; int32_t x = 0;
int x = 0; int32_t y = 0;
int y = 0; int32_t w = 0;
int32_t h = 0;
FontTexturePosition alloc_shelf(int32_t p_id, int32_t p_w, int32_t p_h) {
if (p_w > w || p_h > h) {
return FontTexturePosition(-1, 0, 0);
}
int32_t xx = x;
x += p_w;
w -= p_w;
return FontTexturePosition(p_id, xx, y);
}
Shelf() {}
Shelf(int32_t p_x, int32_t p_y, int32_t p_w, int32_t p_h) :
x(p_x), y(p_y), w(p_w), h(p_h) {}
};
struct ShelfPackTexture {
int32_t texture_w = 1024;
int32_t texture_h = 1024;
Image::Format format;
PackedByteArray imgdata;
Ref<ImageTexture> texture;
bool dirty = true;
List<Shelf> shelves;
FontTexturePosition pack_rect(int32_t p_id, int32_t p_h, int32_t p_w) {
int32_t y = 0;
int32_t waste = 0;
Shelf *best_shelf = nullptr;
int32_t best_waste = std::numeric_limits<std::int32_t>::max();
for (Shelf &E : shelves) {
y += E.h;
if (p_w > E.w) {
continue;
}
if (p_h == E.h) {
return E.alloc_shelf(p_id, p_w, p_h);
}
if (p_h > E.h) {
continue;
}
if (p_h < E.h) {
waste = (E.h - p_h) * p_w;
if (waste < best_waste) {
best_waste = waste;
best_shelf = &E;
}
}
}
if (best_shelf) {
return best_shelf->alloc_shelf(p_id, p_w, p_h);
}
if (p_h <= (texture_h - y) && p_w <= texture_w) {
List<Shelf>::Element *E = shelves.push_back(Shelf(0, y, texture_w, p_h));
return E->get().alloc_shelf(p_id, p_w, p_h);
}
return FontTexturePosition(-1, 0, 0);
}
ShelfPackTexture() {}
ShelfPackTexture(int32_t p_w, int32_t p_h) :
texture_w(p_w), texture_h(p_h) {}
}; };
struct FontGlyph { struct FontGlyph {
@@ -161,7 +227,7 @@ class TextServerFallback : public TextServerExtension {
Vector2i size; Vector2i size;
Vector<FontTexture> textures; Vector<ShelfPackTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map; HashMap<int32_t, FontGlyph> glyph_map;
HashMap<Vector2i, Vector2> kerning_map; HashMap<Vector2i, Vector2> kerning_map;