mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-26 16:15:39 +00:00
Made accurate text layout work correctly using HarfBuzz
This commit is contained in:
parent
7a8bd97226
commit
c873441851
|
@ -484,7 +484,7 @@ void TextComponent::onTextChanged()
|
||||||
std::shared_ptr<Font> font {mFont};
|
std::shared_ptr<Font> font {mFont};
|
||||||
|
|
||||||
// Used to initialize all glyphs, which is needed to populate mMaxGlyphHeight.
|
// Used to initialize all glyphs, which is needed to populate mMaxGlyphHeight.
|
||||||
lineHeight = mFont->loadGlyphs(text + "\n") * mLineSpacing;
|
lineHeight = mFont->loadGlyphs(text) * mLineSpacing;
|
||||||
|
|
||||||
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y * mRelativeScale > lineHeight};
|
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y * mRelativeScale > lineHeight};
|
||||||
float offsetY {0.0f};
|
float offsetY {0.0f};
|
||||||
|
|
|
@ -106,8 +106,7 @@ glm::vec2 Font::sizeText(std::string text, float lineSpacing)
|
||||||
|
|
||||||
for (auto& segment : segmentsHB) {
|
for (auto& segment : segmentsHB) {
|
||||||
for (size_t i {0}; i < segment.glyphIndexes.size(); ++i) {
|
for (size_t i {0}; i < segment.glyphIndexes.size(); ++i) {
|
||||||
const unsigned int character {segment.glyphIndexes[i]};
|
const unsigned int character {segment.glyphIndexes[i].first};
|
||||||
Glyph* glyph {nullptr};
|
|
||||||
|
|
||||||
// Invalid character.
|
// Invalid character.
|
||||||
if (!segment.doShape && character == 0)
|
if (!segment.doShape && character == 0)
|
||||||
|
@ -122,13 +121,7 @@ glm::vec2 Font::sizeText(std::string text, float lineSpacing)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.doShape)
|
lineWidth += segment.glyphIndexes[i].second;
|
||||||
glyph = getGlyphByIndex(character, segment.fontHB);
|
|
||||||
else
|
|
||||||
glyph = getGlyph(character);
|
|
||||||
|
|
||||||
if (glyph)
|
|
||||||
lineWidth += glyph->advance.x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineWidth > highestWidth)
|
if (lineWidth > highestWidth)
|
||||||
|
@ -146,7 +139,7 @@ int Font::loadGlyphs(const std::string& text)
|
||||||
|
|
||||||
for (auto& segment : segmentsHB) {
|
for (auto& segment : segmentsHB) {
|
||||||
for (size_t i {0}; i < segment.glyphIndexes.size(); ++i) {
|
for (size_t i {0}; i < segment.glyphIndexes.size(); ++i) {
|
||||||
const unsigned int character {segment.glyphIndexes[i]};
|
const unsigned int character {segment.glyphIndexes[i].first};
|
||||||
Glyph* glyph {nullptr};
|
Glyph* glyph {nullptr};
|
||||||
|
|
||||||
// Invalid character.
|
// Invalid character.
|
||||||
|
@ -154,7 +147,7 @@ int Font::loadGlyphs(const std::string& text)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (segment.doShape)
|
if (segment.doShape)
|
||||||
glyph = getGlyphByIndex(character, segment.fontHB);
|
glyph = getGlyphByIndex(character, segment.fontHB, segment.glyphIndexes[i].second);
|
||||||
else
|
else
|
||||||
glyph = getGlyph(character);
|
glyph = getGlyph(character);
|
||||||
|
|
||||||
|
@ -207,7 +200,7 @@ TextCache* Font::buildTextCache(const std::string& text,
|
||||||
|
|
||||||
for (auto& segment : segmentsHB) {
|
for (auto& segment : segmentsHB) {
|
||||||
for (size_t cursor {0}; cursor < segment.glyphIndexes.size(); ++cursor) {
|
for (size_t cursor {0}; cursor < segment.glyphIndexes.size(); ++cursor) {
|
||||||
const unsigned int character {segment.glyphIndexes[cursor]};
|
const unsigned int character {segment.glyphIndexes[cursor].first};
|
||||||
Glyph* glyph {nullptr};
|
Glyph* glyph {nullptr};
|
||||||
|
|
||||||
// Invalid character.
|
// Invalid character.
|
||||||
|
@ -225,7 +218,8 @@ TextCache* Font::buildTextCache(const std::string& text,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment.doShape)
|
if (segment.doShape)
|
||||||
glyph = getGlyphByIndex(character, segment.fontHB);
|
glyph =
|
||||||
|
getGlyphByIndex(character, segment.fontHB, segment.glyphIndexes[cursor].second);
|
||||||
else
|
else
|
||||||
glyph = getGlyph(character);
|
glyph = getGlyph(character);
|
||||||
|
|
||||||
|
@ -480,8 +474,7 @@ glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText,
|
||||||
// if (totalPos > stop)
|
// if (totalPos > stop)
|
||||||
// break;
|
// break;
|
||||||
|
|
||||||
// const unsigned int character {segment.glyphIndexes[i]};
|
// const unsigned int character {segment.glyphIndexes[i].first};
|
||||||
// Glyph* glyph {nullptr};
|
|
||||||
|
|
||||||
// // Invalid character.
|
// // Invalid character.
|
||||||
// if (!segment.doShape && character == 0)
|
// if (!segment.doShape && character == 0)
|
||||||
|
@ -493,13 +486,7 @@ glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText,
|
||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if (segment.doShape)
|
// lineWidth += segment.glyphIndexes[i].second;
|
||||||
// glyph = getGlyphByIndex(character, segment.fontHB);
|
|
||||||
// else
|
|
||||||
// glyph = getGlyph(character);
|
|
||||||
|
|
||||||
// if (glyph)
|
|
||||||
// lineWidth += glyph->advance.x;
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -853,13 +840,12 @@ std::vector<Font::ShapeSegment> Font::shapeText(const std::string& text)
|
||||||
|
|
||||||
if (segment.doShape) {
|
if (segment.doShape) {
|
||||||
character = glyphInfo[cursor].codepoint;
|
character = glyphInfo[cursor].codepoint;
|
||||||
// As HarfBuzz sometimes incorrectly indicates a zero advance we need to get the
|
getGlyphByIndex(character, segment.fontHB == nullptr ? mFontHB : segment.fontHB,
|
||||||
// advance value from the glyph entry as it will in this case fall back to the
|
glyphPos[cursor].x_advance);
|
||||||
// built-in font advance value for the glyph.
|
const int advanceX {static_cast<int>(
|
||||||
Glyph* glyph {getGlyphByIndex(character,
|
std::round(static_cast<float>(glyphPos[cursor].x_advance) / 256.0f))};
|
||||||
segment.fontHB == nullptr ? mFontHB : segment.fontHB,
|
segment.glyphsWidth += advanceX;
|
||||||
glyphPos[cursor].x_advance)};
|
segment.glyphIndexes.emplace_back(std::make_pair(character, advanceX));
|
||||||
segment.glyphsWidth += glyph->advance.x;
|
|
||||||
++cursor;
|
++cursor;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -867,9 +853,8 @@ std::vector<Font::ShapeSegment> Font::shapeText(const std::string& text)
|
||||||
character = Utils::String::chars2Unicode(segment.substring, cursor);
|
character = Utils::String::chars2Unicode(segment.substring, cursor);
|
||||||
Glyph* glyph {getGlyph(character)};
|
Glyph* glyph {getGlyph(character)};
|
||||||
segment.glyphsWidth += glyph->advance.x;
|
segment.glyphsWidth += glyph->advance.x;
|
||||||
|
segment.glyphIndexes.emplace_back(std::make_pair(character, glyph->advance.x));
|
||||||
}
|
}
|
||||||
|
|
||||||
segment.glyphIndexes.emplace_back(character);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,11 +889,11 @@ void Font::rebuildTextures()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = mGlyphMapByIndex.cbegin(); it != mGlyphMapByIndex.cend(); ++it) {
|
for (auto it = mGlyphMapByIndex.cbegin(); it != mGlyphMapByIndex.cend(); ++it) {
|
||||||
FT_Face* face {getFaceForGlyphIndex(it->first.first, it->first.second)};
|
FT_Face* face {getFaceForGlyphIndex(std::get<0>(it->first), std::get<1>(it->first))};
|
||||||
FT_GlyphSlot glyphSlot {(*face)->glyph};
|
FT_GlyphSlot glyphSlot {(*face)->glyph};
|
||||||
|
|
||||||
// Load the glyph bitmap through FreeType.
|
// Load the glyph bitmap through FreeType.
|
||||||
FT_Load_Glyph(*face, it->first.first, FT_LOAD_RENDER);
|
FT_Load_Glyph(*face, std::get<0>(it->first), FT_LOAD_RENDER);
|
||||||
|
|
||||||
const glm::ivec2 glyphSize {glyphSlot->bitmap.width, glyphSlot->bitmap.rows};
|
const glm::ivec2 glyphSize {glyphSlot->bitmap.width, glyphSlot->bitmap.rows};
|
||||||
const glm::ivec2 cursor {
|
const glm::ivec2 cursor {
|
||||||
|
@ -1071,8 +1056,8 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
|
||||||
Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, int xAdvance)
|
Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, int xAdvance)
|
||||||
{
|
{
|
||||||
// Check if the glyph has already been loaded.
|
// Check if the glyph has already been loaded.
|
||||||
auto it = mGlyphMapByIndex.find(std::make_pair(id, fontArg));
|
auto it = mGlyphMapByIndex.find(std::make_tuple(id, fontArg, xAdvance));
|
||||||
if (it != mGlyphMapByIndex.cend())
|
if (it != mGlyphMapByIndex.end())
|
||||||
return &it->second;
|
return &it->second;
|
||||||
|
|
||||||
// We need to create a new entry.
|
// We need to create a new entry.
|
||||||
|
@ -1117,7 +1102,7 @@ Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, in
|
||||||
mLetterHeight = static_cast<float>(glyphSize.y);
|
mLetterHeight = static_cast<float>(glyphSize.y);
|
||||||
|
|
||||||
// Create glyph.
|
// Create glyph.
|
||||||
Glyph& glyph {mGlyphMapByIndex[std::make_pair(id, mLastFontHB)]};
|
Glyph& glyph {mGlyphMapByIndex[std::make_tuple(id, mLastFontHB, xAdvance)]};
|
||||||
|
|
||||||
glyph.fontHB = mLastFontHB;
|
glyph.fontHB = mLastFontHB;
|
||||||
glyph.texture = tex;
|
glyph.texture = tex;
|
||||||
|
@ -1125,13 +1110,7 @@ Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, in
|
||||||
cursor.y / static_cast<float>(tex->textureSize.y)};
|
cursor.y / static_cast<float>(tex->textureSize.y)};
|
||||||
glyph.texSize = {glyphSize.x / static_cast<float>(tex->textureSize.x),
|
glyph.texSize = {glyphSize.x / static_cast<float>(tex->textureSize.x),
|
||||||
glyphSize.y / static_cast<float>(tex->textureSize.y)};
|
glyphSize.y / static_cast<float>(tex->textureSize.y)};
|
||||||
// Sometimes HarfBuzz incorrectly indicates a zero advance so in this case we need to fall back
|
glyph.advance = {xAdvance, glyphSlot->metrics.vertAdvance >> 6};
|
||||||
// to the font-default advance value for the glyph.
|
|
||||||
if (xAdvance == 0)
|
|
||||||
glyph.advance = {glyphSlot->metrics.horiAdvance >> 6, glyphSlot->metrics.vertAdvance >> 6};
|
|
||||||
else
|
|
||||||
glyph.advance = {static_cast<int>(std::round(static_cast<float>(xAdvance) / 256.0f)),
|
|
||||||
glyphSlot->metrics.vertAdvance >> 6};
|
|
||||||
glyph.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6};
|
glyph.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6};
|
||||||
glyph.rows = glyphSize.y;
|
glyph.rows = glyphSize.y;
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,7 @@ private:
|
||||||
bool doShape;
|
bool doShape;
|
||||||
bool rightToLeft;
|
bool rightToLeft;
|
||||||
std::string substring;
|
std::string substring;
|
||||||
std::vector<unsigned int> glyphIndexes;
|
std::vector<std::pair<unsigned int, int>> glyphIndexes;
|
||||||
|
|
||||||
ShapeSegment()
|
ShapeSegment()
|
||||||
: startPos {0}
|
: startPos {0}
|
||||||
|
@ -224,7 +224,7 @@ private:
|
||||||
FT_Face* getFaceForChar(unsigned int id);
|
FT_Face* getFaceForChar(unsigned int id);
|
||||||
FT_Face* getFaceForGlyphIndex(unsigned int id, hb_font_t* fontArg);
|
FT_Face* getFaceForGlyphIndex(unsigned int id, hb_font_t* fontArg);
|
||||||
Glyph* getGlyph(const unsigned int id);
|
Glyph* getGlyph(const unsigned int id);
|
||||||
Glyph* getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, int xAdvance = 0);
|
Glyph* getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, int xAdvance);
|
||||||
|
|
||||||
float getNewlineStartOffset(const std::string& text,
|
float getNewlineStartOffset(const std::string& text,
|
||||||
const unsigned int& charStart,
|
const unsigned int& charStart,
|
||||||
|
@ -239,7 +239,7 @@ private:
|
||||||
std::unique_ptr<FontFace> mFontFace;
|
std::unique_ptr<FontFace> mFontFace;
|
||||||
std::vector<std::unique_ptr<FontTexture>> mTextures;
|
std::vector<std::unique_ptr<FontTexture>> mTextures;
|
||||||
std::map<unsigned int, Glyph> mGlyphMap;
|
std::map<unsigned int, Glyph> mGlyphMap;
|
||||||
std::map<std::pair<unsigned int, hb_font_t*>, Glyph> mGlyphMapByIndex;
|
std::map<std::tuple<unsigned int, hb_font_t*, int>, Glyph> mGlyphMapByIndex;
|
||||||
|
|
||||||
const std::string mPath;
|
const std::string mPath;
|
||||||
hb_font_t* mFontHB;
|
hb_font_t* mFontHB;
|
||||||
|
|
Loading…
Reference in a new issue