perf(font): cache per-codepoint advances in font units (#57)
Font::GetLineWidth called stbtt_GetCodepointHMetrics for every glyph on every invocation. InputField_HitTestCursor rescans the whole field once per character, making caret hit-testing O(n²) in HMetrics lookups. Memoise advances per Font via Font::AdvanceUnits: ASCII in a flat array, the rest in a map. Advances are cached in unscaled font *units* and rescaled per call — Glyph::advance in the FontAtlas is baked at kBaseSize and is wrong at any other size, so it cannot be reused. The per-glyph (int) truncation in GetLineWidth is preserved exactly. Adds the FontAdvanceCache CPU-only regression test covering size independence of the cached unit advance, the per-glyph-truncated rescale pipeline, cache-hit determinism, width scaling with size, and the non-ASCII map path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c1bd5fe244
commit
c169986835
5 changed files with 203 additions and 3 deletions
|
|
@ -56,6 +56,23 @@ Font::Font(const std::filesystem::path& fontFilePath) {
|
|||
this->ascent = ascent;
|
||||
this->descent = descent;
|
||||
this->lineGap = lineGap;
|
||||
|
||||
asciiAdvance_.fill(-1);
|
||||
}
|
||||
|
||||
std::int32_t Font::AdvanceUnits(std::uint32_t cp) {
|
||||
if (cp < asciiAdvance_.size()) {
|
||||
if (asciiAdvance_[cp] < 0) {
|
||||
int advance = 0, lsb = 0;
|
||||
stbtt_GetCodepointHMetrics(&font, static_cast<int>(cp), &advance, &lsb);
|
||||
asciiAdvance_[cp] = advance;
|
||||
}
|
||||
return asciiAdvance_[cp];
|
||||
}
|
||||
if (auto it = advanceUnits_.find(cp); it != advanceUnits_.end()) return it->second;
|
||||
int advance = 0, lsb = 0;
|
||||
stbtt_GetCodepointHMetrics(&font, static_cast<int>(cp), &advance, &lsb);
|
||||
return advanceUnits_.emplace(cp, advance).first->second;
|
||||
}
|
||||
|
||||
std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
|
||||
|
|
@ -65,9 +82,7 @@ std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
|
|||
while (i < text.size()) {
|
||||
std::uint32_t cp = DecodeUtf8(text, i);
|
||||
if (cp == 0) break;
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&font, static_cast<int>(cp), &advance, &lsb);
|
||||
lineWidth += (int)(advance * scale);
|
||||
lineWidth += (int)(AdvanceUnits(cp) * scale);
|
||||
}
|
||||
return lineWidth;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue