Skip to content

Commit 81478c1

Browse files
committed
[text] Fix baseline/text height for SpriteTextBlob when fallback font is used
We've added a new TextBlob::textHeight() field, and calculate the baseline correctly first to shape all glyphs based on that baseline later. Needed to fix aseprite/aseprite#5210
1 parent 7d30a58 commit 81478c1

File tree

4 files changed

+90
-24
lines changed

4 files changed

+90
-24
lines changed

text/font.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace text {
2626

2727
class Font : public base::RefCount {
2828
public:
29-
Font() : m_fallback(nullptr) {}
29+
Font() {}
3030
virtual ~Font() {}
3131
virtual FontType type() = 0;
3232
virtual TypefaceRef typeface() const = 0;
@@ -54,11 +54,11 @@ class Font : public base::RefCount {
5454
virtual gfx::RectF getGlyphBounds(glyph_t glyph) const = 0;
5555
virtual float getGlyphAdvance(glyph_t glyph) const = 0;
5656

57-
Font* fallback() const { return m_fallback; }
58-
void setFallback(Font* font) { m_fallback = font; }
57+
FontRef fallback() const { return m_fallback; }
58+
void setFallback(FontRef font) { m_fallback = font; }
5959

6060
private:
61-
Font* m_fallback;
61+
FontRef m_fallback;
6262
};
6363

6464
} // namespace text

text/sprite_text_blob_shaper.cpp

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,66 @@ TextBlobRef SpriteTextBlob::MakeWithShaper(const FontMgrRef& fontMgr,
6969

7070
const auto* spriteFont = static_cast<const SpriteSheetFont*>(font.get());
7171

72-
float baseline = 0.0f;
72+
// TODO add configuration of the default falback font
73+
auto getFallbackFont = [](const FontMgrRef& fontMgr, const FontRef& font) -> FontRef {
74+
FontRef fallbackFont = font->fallback();
75+
if (!fallbackFont) {
76+
fallbackFont = fontMgr->defaultFont();
77+
fallbackFont->setSize(font->size());
78+
fallbackFont->setAntialias(font->antialias());
79+
}
80+
return fallbackFont;
81+
};
82+
83+
// First iteration through the text/fonts/fallback fonts to
84+
// calculate the total text height and baseline to be used.
85+
FontMetrics metrics;
86+
font->metrics(&metrics);
87+
float baseline = -metrics.ascent;
88+
float textHeight = metrics.descent - metrics.ascent;
89+
{
90+
// TODO this utf8 iteration between fonts/fallback is duplicated
91+
// here and below, we could try to merge this code
92+
base::utf8_decode decode(text);
93+
while (true) {
94+
const codepoint_t chr = decode.next();
95+
if (chr == 0)
96+
break;
97+
98+
const glyph_t glyph = spriteFont->codePointToGlyph(chr);
99+
// Code point not found, use the fallback font or the FontMgr and
100+
// create a run using another TextBlob.
101+
if (glyph == 0) {
102+
base::utf8_decode subDecode = decode;
103+
while (true) {
104+
const base::utf8_decode prevSubDecode = subDecode;
105+
const codepoint_t subChr = subDecode.next();
106+
if (subChr == 0) {
107+
decode = subDecode;
108+
break;
109+
}
110+
111+
// Continue the run until we find a glyph that can be
112+
// represent with the original font.
113+
if (spriteFont->codePointToGlyph(subChr) != 0) {
114+
decode = prevSubDecode; // Go back to the previous decode point
115+
break;
116+
}
117+
}
118+
119+
// Calculate the max baseline/textHeight
120+
FontRef fallbackFont = getFallbackFont(fontMgr, font);
121+
FontMetrics fallbackMetrics;
122+
fallbackFont->metrics(&fallbackMetrics);
123+
baseline = std::max(baseline, -fallbackMetrics.ascent);
124+
textHeight = std::max(textHeight, fallbackMetrics.descent - fallbackMetrics.ascent);
125+
}
126+
}
127+
}
128+
73129
Runs runs;
74130
Run run;
75-
auto addRun = [&runs, &run, &font, &baseline, handler]() {
131+
auto addRun = [&runs, &run, &font, handler]() {
76132
if (handler && !run.subBlob) {
77133
TextBlob::RunInfo info;
78134

@@ -83,10 +139,6 @@ TextBlobRef SpriteTextBlob::MakeWithShaper(const FontMgrRef& fontMgr,
83139
info.positions = run.positions.data();
84140
info.clusters = run.clusters.data();
85141

86-
FontMetrics metrics;
87-
font->metrics(&metrics);
88-
baseline = std::max(baseline, -metrics.ascent);
89-
90142
handler->commitRunBuffer(info);
91143
}
92144
runs.push_back(run);
@@ -134,25 +186,15 @@ TextBlobRef SpriteTextBlob::MakeWithShaper(const FontMgrRef& fontMgr,
134186
run.utf8Range.begin = i;
135187
run.utf8Range.end = j;
136188

137-
// TODO add configuration of the default fallback font
138-
auto fallbackFont = fontMgr->defaultFont();
139-
fallbackFont->setSize(font->size());
140-
fallbackFont->setAntialias(font->antialias());
141-
142189
// Align position between both fonts (font and fallbackFont)
143190
// in the baseline pos of the original font.
144-
FontMetrics metrics;
191+
FontRef fallbackFont = getFallbackFont(fontMgr, font);
145192
FontMetrics fallbackMetrics;
146-
font->metrics(&metrics);
147193
fallbackFont->metrics(&fallbackMetrics);
148194

149195
gfx::PointF alignedPos;
150196
alignedPos.x = pos.x;
151-
const float baselineShift = -metrics.ascent + fallbackMetrics.ascent;
152-
alignedPos.y = pos.y + baselineShift;
153-
154-
// Adjust baseline for this composed TextBlob
155-
baseline = std::max(baseline, -fallbackMetrics.ascent + baselineShift);
197+
alignedPos.y = pos.y + baseline + fallbackMetrics.ascent;
156198

157199
OffsetHandler subHandler(handler, i, alignedPos);
158200
run.subBlob = TextBlob::MakeWithShaper(fontMgr,
@@ -169,15 +211,18 @@ TextBlobRef SpriteTextBlob::MakeWithShaper(const FontMgrRef& fontMgr,
169211
}
170212

171213
// Restore beginning of UTF8 range for the next run
172-
run.utf8Range.begin = decode.pos() - text.begin();
214+
run.utf8Range.begin = j;
173215
continue;
174216
}
175217

176218
gfx::Rect glyphBounds = spriteFont->getGlyphBounds(glyph);
177219
if (glyphBounds.isEmpty())
178220
continue;
179221

180-
run.add(glyph, pos, i - run.utf8Range.begin);
222+
gfx::PointF alignedPos;
223+
alignedPos.x = pos.x;
224+
alignedPos.y = pos.y + baseline + metrics.ascent;
225+
run.add(glyph, alignedPos, i - run.utf8Range.begin);
181226

182227
glyphBounds.offset(pos);
183228
textBounds |= glyphBounds;
@@ -190,6 +235,7 @@ TextBlobRef SpriteTextBlob::MakeWithShaper(const FontMgrRef& fontMgr,
190235

191236
auto blob = base::make_ref<SpriteTextBlob>(textBounds, font, std::move(runs));
192237
blob->setBaseline(baseline);
238+
blob->setTextHeight(textHeight);
193239
return blob;
194240
}
195241

text/text_blob.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ float TextBlob::baseline()
5050
return m_baseline;
5151
}
5252

53+
float TextBlob::textHeight()
54+
{
55+
if (m_textHeight == 0.0f) {
56+
visitRuns([this](RunInfo& info) {
57+
if (!info.font)
58+
return;
59+
60+
FontMetrics metrics;
61+
info.font->metrics(&metrics);
62+
m_textHeight = std::max(m_textHeight, metrics.descent - metrics.ascent);
63+
});
64+
}
65+
return m_textHeight;
66+
}
67+
5368
TextBlob::Utf8Range TextBlob::RunInfo::getGlyphUtf8Range(size_t i) const
5469
{
5570
Utf8Range subRange;

text/text_blob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class TextBlob : public base::RefCount {
7474
// font on each run).
7575
float baseline();
7676

77+
// Returns the max(descent - ascent) for all fonts used in this text blob.
78+
float textHeight();
79+
7780
// Visits each run in the TextBlob.
7881
using RunVisitor = std::function<void(RunInfo&)>;
7982
virtual void visitRuns(const RunVisitor& visitor) = 0;
@@ -101,10 +104,12 @@ class TextBlob : public base::RefCount {
101104

102105
protected:
103106
void setBaseline(const float baseline) { m_baseline = baseline; }
107+
void setTextHeight(const float textHeight) { m_textHeight = textHeight; }
104108

105109
private:
106110
gfx::RectF m_bounds;
107111
float m_baseline = 0.0f;
112+
float m_textHeight = 0.0f;
108113
};
109114

110115
} // namespace text

0 commit comments

Comments
 (0)