mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-03-06 14:27:43 +00:00
Moved the HarfBuzz segment building to a separate function
Also implemented segment caching and fixed an issue where missing glyphs were not handled correctly
This commit is contained in:
parent
82f6686cbf
commit
412e74738a
|
@ -24,6 +24,7 @@ Font::Font(float size, const std::string& path)
|
|||
, mFontSize {size}
|
||||
, mLetterHeight {0.0f}
|
||||
, mMaxGlyphHeight {static_cast<int>(std::round(size))}
|
||||
, mTextHash {0}
|
||||
{
|
||||
if (mFontSize < 3.0f) {
|
||||
mFontSize = 3.0f;
|
||||
|
@ -174,68 +175,8 @@ TextCache* Font::buildTextCache(const std::string& text,
|
|||
// Vertices by texture.
|
||||
std::map<FontTexture*, std::vector<Renderer::Vertex>> vertMap;
|
||||
|
||||
// HarfBuzz segments.
|
||||
std::vector<ShapeSegment> segmentsHB;
|
||||
|
||||
{
|
||||
hb_font_t* lastFont {nullptr};
|
||||
unsigned int lastCursor {0};
|
||||
unsigned int byteLength {0};
|
||||
bool addSegment {false};
|
||||
bool shapeSegment {true};
|
||||
bool lastWasNoShaping {false};
|
||||
size_t textCursor {0};
|
||||
size_t lastFlushPos {0};
|
||||
|
||||
while (textCursor < text.length()) {
|
||||
addSegment = false;
|
||||
shapeSegment = true;
|
||||
lastCursor = textCursor;
|
||||
const unsigned int unicode {Utils::String::chars2Unicode(text, textCursor)};
|
||||
Glyph* currGlyph {getGlyph(unicode)};
|
||||
byteLength = textCursor - lastCursor;
|
||||
|
||||
if (unicode == '\'' || unicode == '\n') {
|
||||
// HarfBuzz converts ' and newline characters to invalid characters, so we
|
||||
// need to exclude these from getting shaped. This means adding a new segment.
|
||||
addSegment = true;
|
||||
if (!lastWasNoShaping) {
|
||||
textCursor -= byteLength;
|
||||
if (lastFlushPos == textCursor)
|
||||
addSegment = false;
|
||||
lastWasNoShaping = true;
|
||||
}
|
||||
else {
|
||||
shapeSegment = false;
|
||||
lastWasNoShaping = false;
|
||||
}
|
||||
}
|
||||
else if (textCursor == text.length()) {
|
||||
// Last (and possibly only) segment for this text.
|
||||
addSegment = true;
|
||||
}
|
||||
else if (lastFont != nullptr && lastFont != currGlyph->fontHB) {
|
||||
// The font changed, which requires a new segment.
|
||||
addSegment = true;
|
||||
textCursor -= byteLength;
|
||||
}
|
||||
|
||||
if (addSegment) {
|
||||
ShapeSegment segment;
|
||||
segment.startPos = lastFlushPos;
|
||||
segment.length = textCursor - lastFlushPos;
|
||||
segment.fontHB = (lastFont == nullptr ? currGlyph->fontHB : lastFont);
|
||||
segment.doShape = shapeSegment;
|
||||
if (!shapeSegment)
|
||||
segment.substring = text.substr(lastFlushPos, textCursor - lastFlushPos);
|
||||
|
||||
segmentsHB.emplace_back(std::move(segment));
|
||||
|
||||
lastFlushPos = textCursor;
|
||||
}
|
||||
lastFont = currGlyph->fontHB;
|
||||
}
|
||||
}
|
||||
// Build segments for HarfBuzz.
|
||||
buildShapeSegments(text);
|
||||
|
||||
size_t cursor {0};
|
||||
size_t length {0};
|
||||
|
@ -243,7 +184,7 @@ TextCache* Font::buildTextCache(const std::string& text,
|
|||
hb_glyph_position_t* glyphPos {nullptr};
|
||||
unsigned int glyphCount {0};
|
||||
|
||||
for (auto& segment : segmentsHB) {
|
||||
for (auto& segment : mSegmentsHB) {
|
||||
cursor = 0;
|
||||
length = 0;
|
||||
|
||||
|
@ -638,7 +579,9 @@ std::vector<Font::FallbackFontCache> Font::getFallbackFontPaths()
|
|||
// Korean
|
||||
":/fonts/NanumMyeongjo.ttf",
|
||||
// Font Awesome icon glyphs, used for various special symbols like stars, folders etc.
|
||||
":/fonts/fontawesome-webfont.ttf", ":/fonts/NotoEmoji.ttf"};
|
||||
":/fonts/fontawesome-webfont.ttf",
|
||||
// Google Noto Emoji.
|
||||
":/fonts/NotoEmoji.ttf"};
|
||||
|
||||
for (auto& font : fallbackFonts) {
|
||||
FallbackFontCache fallbackFont;
|
||||
|
@ -754,6 +697,78 @@ void Font::initLibrary()
|
|||
}
|
||||
}
|
||||
|
||||
void Font::buildShapeSegments(const std::string& text)
|
||||
{
|
||||
// Calculate the hash value for the string to make sure we're not building segments
|
||||
// repeatedly for the same text.
|
||||
const size_t hashValue {std::hash<std::string> {}(text)};
|
||||
if (hashValue == mTextHash)
|
||||
return;
|
||||
|
||||
mTextHash = hashValue;
|
||||
mSegmentsHB.clear();
|
||||
|
||||
hb_font_t* lastFont {nullptr};
|
||||
unsigned int lastCursor {0};
|
||||
unsigned int byteLength {0};
|
||||
bool addSegment {false};
|
||||
bool shapeSegment {true};
|
||||
bool lastWasNoShaping {false};
|
||||
size_t textCursor {0};
|
||||
size_t lastFlushPos {0};
|
||||
|
||||
while (textCursor < text.length()) {
|
||||
addSegment = false;
|
||||
shapeSegment = true;
|
||||
lastCursor = textCursor;
|
||||
const unsigned int unicode {Utils::String::chars2Unicode(text, textCursor)};
|
||||
Glyph* currGlyph {getGlyph(unicode)};
|
||||
byteLength = textCursor - lastCursor;
|
||||
|
||||
if (unicode == '\'' || unicode == '\n' || currGlyph->fontHB == nullptr) {
|
||||
// HarfBuzz converts ' and newline characters to invalid characters, so we
|
||||
// need to exclude these from getting shaped. This means adding a new segment.
|
||||
// We also add a segment if there is no font set as it means there was a missing
|
||||
// glyph and the "no glyph" symbol should be shown.
|
||||
addSegment = true;
|
||||
if (!lastWasNoShaping) {
|
||||
textCursor -= byteLength;
|
||||
if (lastFlushPos == textCursor)
|
||||
addSegment = false;
|
||||
lastWasNoShaping = true;
|
||||
}
|
||||
else {
|
||||
shapeSegment = false;
|
||||
lastWasNoShaping = false;
|
||||
}
|
||||
}
|
||||
else if (textCursor == text.length()) {
|
||||
// Last (and possibly only) segment for this text.
|
||||
addSegment = true;
|
||||
}
|
||||
else if (lastFont != nullptr && lastFont != currGlyph->fontHB) {
|
||||
// The font changed, which requires a new segment.
|
||||
addSegment = true;
|
||||
textCursor -= byteLength;
|
||||
}
|
||||
|
||||
if (addSegment) {
|
||||
ShapeSegment segment;
|
||||
segment.startPos = lastFlushPos;
|
||||
segment.length = textCursor - lastFlushPos;
|
||||
segment.fontHB = (lastFont == nullptr ? currGlyph->fontHB : lastFont);
|
||||
segment.doShape = shapeSegment;
|
||||
if (!shapeSegment)
|
||||
segment.substring = text.substr(lastFlushPos, textCursor - lastFlushPos);
|
||||
|
||||
mSegmentsHB.emplace_back(std::move(segment));
|
||||
|
||||
lastFlushPos = textCursor;
|
||||
}
|
||||
lastFont = currGlyph->fontHB;
|
||||
}
|
||||
}
|
||||
|
||||
void Font::rebuildTextures()
|
||||
{
|
||||
// Recreate OpenGL textures.
|
||||
|
@ -850,7 +865,8 @@ FT_Face* Font::getFaceForChar(unsigned int id)
|
|||
}
|
||||
}
|
||||
|
||||
// Couldn't find a valid glyph, return the current font face so we get a "missing" character.
|
||||
// Couldn't find a valid glyph, return the current font face so we get a "no glyph" character.
|
||||
mLastFontHB = nullptr;
|
||||
return &mFontFace->face;
|
||||
}
|
||||
|
||||
|
@ -870,7 +886,8 @@ FT_Face* Font::getFaceForGlyphIndex(unsigned int id, hb_font_t* fontArg)
|
|||
}
|
||||
}
|
||||
|
||||
// Couldn't find a valid glyph, return the current font face so we get a "missing" character.
|
||||
// Couldn't find a valid glyph, return the current font face so we get a "no glyph" character.
|
||||
mLastFontHB = nullptr;
|
||||
return &mFontFace->face;
|
||||
}
|
||||
|
||||
|
|
|
@ -204,6 +204,9 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
// Builds segments for HarfBuzz.
|
||||
void buildShapeSegments(const std::string& text);
|
||||
|
||||
// Completely recreate the texture data for all textures based on mGlyphs information.
|
||||
void rebuildTextures();
|
||||
void unloadTextures();
|
||||
|
@ -232,6 +235,7 @@ private:
|
|||
std::vector<std::unique_ptr<FontTexture>> mTextures;
|
||||
std::map<unsigned int, Glyph> mGlyphMap;
|
||||
std::map<std::pair<unsigned int, hb_font_t*>, Glyph> mGlyphMapByIndex;
|
||||
std::vector<ShapeSegment> mSegmentsHB;
|
||||
|
||||
const std::string mPath;
|
||||
hb_font_t* mFontHB;
|
||||
|
@ -241,6 +245,7 @@ private:
|
|||
float mFontSize;
|
||||
float mLetterHeight;
|
||||
int mMaxGlyphHeight;
|
||||
size_t mTextHash;
|
||||
};
|
||||
|
||||
// Used to store a sort of "pre-rendered" string.
|
||||
|
|
Loading…
Reference in a new issue