From a7d673f541f1dc272845dfa8d53ae1de826eb265 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Aug 2024 13:43:44 +0200 Subject: [PATCH] Some font-related code and comments cleanup --- es-core/src/resources/Font.cpp | 49 ++++++++++------------------------ es-core/src/resources/Font.h | 22 +++++---------- 2 files changed, 20 insertions(+), 51 deletions(-) diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 88ae99c51..51dc82b6c 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -3,8 +3,7 @@ // ES-DE Frontend // Font.h // -// Loading, unloading, caching, shaping and rendering of fonts. -// Also functions for text wrapping and similar. +// Font management and text shaping and rendering. // #include "resources/Font.h" @@ -654,7 +653,7 @@ Font::FontTexture::FontTexture(const int mFontSize) rowHeight = 0; writePos = glm::ivec2 {1, 1}; - // Set the texture atlas to a reasonable size, if we run out of space for adding glyphs then + // Set the glyph atlas to a reasonable size, if we run out of space for adding glyphs then // more textures will be created dynamically. textureSize = glm::ivec2 {mFontSize * 6, mFontSize * 6}; } @@ -695,7 +694,7 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursorOut) void Font::FontTexture::initTexture() { assert(textureId == 0); - // Create a black texture with zero alpha value so that the single-pixel spaces between the + // Create a black texture with a zero alpha value so that single-pixel spaces between the // glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels // would get sampled during scaling. std::vector texture(textureSize.x * textureSize.y * 4, 0); @@ -722,7 +721,7 @@ Font::FontFace::FontFace(ResourceData&& d, float size, const std::string& path, // Even though a fractional font size can be requested, the glyphs will always be rounded // to integers. It's not useless to call FT_Set_Char_Size() instead of FT_Set_Pixel_Sizes() - // though as the glyphs will still be much more evenely sized across different resolutions. + // though as the glyphs will still be much more evenly sized across different resolutions. FT_Set_Char_Size(face, static_cast(0.0f), static_cast(size * 64.0f), 0, 0); fontHB = fontArg; @@ -867,6 +866,7 @@ void Font::shapeText(const std::string& text, std::vector& segment else { // This also advances the cursor. character = Utils::String::chars2Unicode(segment.substring, cursor); + Glyph* glyph {getGlyph(character)}; segment.shapedWidth += glyph->advance.x; segment.glyphIndexes.emplace_back(std::make_pair(character, glyph->advance.x)); @@ -877,7 +877,7 @@ void Font::shapeText(const std::string& text, std::vector& segment void Font::rebuildTextures() { - // Recreate OpenGL textures. + // Recreate all glyph atlas textures. for (auto it = mTextures.begin(); it != mTextures.end(); ++it) (*it)->initTexture(); @@ -909,7 +909,6 @@ void Font::rebuildTextures() getFaceForGlyphIndex(std::get<0>(it->first), std::get<1>(it->first), &returnedFont)}; FT_GlyphSlot glyphSlot {(*face)->glyph}; - // Load the glyph bitmap through FreeType. FT_Load_Glyph(*face, std::get<0>(it->first), FT_LOAD_RENDER); const glm::ivec2 glyphSize {glyphSlot->bitmap.width, glyphSlot->bitmap.rows}; @@ -917,7 +916,6 @@ void Font::rebuildTextures() static_cast(it->second.texPos.x * it->second.texture->textureSize.x), static_cast(it->second.texPos.y * it->second.texture->textureSize.y)}; - // Upload glyph bitmap to texture. if (glyphSize.x > 0 && glyphSize.y > 0) { mRenderer->updateTexture(it->second.texture->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y, glyphSize.x, glyphSize.y, @@ -995,7 +993,6 @@ FT_Face* Font::getFaceForGlyphIndex(unsigned int id, hb_font_t* fontArg, hb_font } } - // Couldn't find a valid glyph, return the current font face so we get a "no glyph" character. *returnedFont = nullptr; return &mFontFace->face; } @@ -1019,16 +1016,7 @@ Font::Glyph* Font::getGlyph(const unsigned int id) const FT_GlyphSlot glyphSlot {(*face)->glyph}; - // If the font does not contain hinting information then force the use of the automatic - // hinter that is built into FreeType. Note: Using font-supplied hints generally looks worse - // than using the auto-hinter so it's disabled for now. - // const bool hasHinting {static_cast(glyphSlot->face->face_flags & FT_FACE_FLAG_HINTER)}; - const bool hasHinting {true}; - - if (FT_Load_Char(*face, id, - (hasHinting ? - FT_LOAD_RENDER : - FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT))) { + if (FT_Load_Char(*face, id, FT_LOAD_RENDER)) { LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath << ", size " << mFontSize; return nullptr; @@ -1063,7 +1051,7 @@ Font::Glyph* Font::getGlyph(const unsigned int id) glyph.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6}; glyph.rows = glyphSize.y; - // Upload glyph bitmap to texture. + // Upload glyph bitmap to glyph atlas texture. if (glyphSize.x > 0 && glyphSize.y > 0) { mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y, glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer); @@ -1084,24 +1072,15 @@ Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, in // We need to create a new entry. FT_Face* face {getFaceForGlyphIndex(id, fontArg, &returnedFont)}; if (!face) { - LOG(LogError) << "Couldn't find appropriate font face for character " << id << " for font " - << mPath; + LOG(LogError) << "Couldn't find appropriate font face for glyph index " << id + << " for font " << mPath; return nullptr; } const FT_GlyphSlot glyphSlot {(*face)->glyph}; - // If the font does not contain hinting information then force the use of the automatic - // hinter that is built into FreeType. Note: Using font-supplied hints generally looks worse - // than using the auto-hinter so it's disabled for now. - // const bool hasHinting {static_cast(glyphSlot->face->face_flags & FT_FACE_FLAG_HINTER)}; - const bool hasHinting {true}; - - if (FT_Load_Glyph(*face, id, - (hasHinting ? - FT_LOAD_RENDER : - FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT))) { - LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath + if (FT_Load_Glyph(*face, id, FT_LOAD_RENDER)) { + LOG(LogError) << "Couldn't find glyph for glyph index " << id << " for font " << mPath << ", size " << mFontSize; return nullptr; } @@ -1126,7 +1105,7 @@ Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, in // This should (hopefully) never occur as size constraints are enforced earlier on. if (tex == nullptr) { - LOG(LogError) << "Couldn't create glyph for character " << id << " for font " << mPath + LOG(LogError) << "Couldn't create glyph for glyph index " << id << " for font " << mPath << ", size " << mFontSize << " (no suitable texture found)"; return nullptr; } @@ -1148,7 +1127,7 @@ Font::Glyph* Font::getGlyphByIndex(const unsigned int id, hb_font_t* fontArg, in glyph.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6}; glyph.rows = glyphSize.y; - // Upload glyph bitmap to texture. + // Upload glyph bitmap to glyph atlas texture. if (glyphSize.x > 0 && glyphSize.y > 0) { mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y, glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer); diff --git a/es-core/src/resources/Font.h b/es-core/src/resources/Font.h index 608db3c12..26a59abca 100644 --- a/es-core/src/resources/Font.h +++ b/es-core/src/resources/Font.h @@ -3,8 +3,7 @@ // ES-DE Frontend // Font.h // -// Loading, unloading, caching, shaping and rendering of fonts. -// Also functions for text wrapping and similar. +// Font management and text shaping and rendering. // #ifndef ES_CORE_RESOURCES_FONT_H @@ -33,8 +32,6 @@ class TextCache; #define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf" #define FONT_PATH_BOLD ":/fonts/Akrobat-Bold.ttf" -// A TrueType Font renderer that uses FreeType and OpenGL. -// The library is automatically initialized when it's needed. class Font : public IReloadable { public: @@ -135,9 +132,9 @@ public: const float sizeMultiplier = 1.0f, const bool fontSizeDimmed = false); - // Returns an approximation of VRAM used by this font's texture (in bytes). + // Returns an approximation of VRAM used by the glyph atlas textures for this font object. size_t getMemUsage() const; - // Returns an approximation of total VRAM used by font textures (in bytes). + // Returns an approximation of VRAM used by the glyph atlas textures for all font objects. static size_t getTotalMemUsage(); private: @@ -155,11 +152,9 @@ private: bool findEmpty(const glm::ivec2& size, glm::ivec2& cursorOut); // You must call initTexture() after creating a FontTexture to get a textureId. - // Initializes the OpenGL texture according to this FontTexture's settings, - // updating textureId. void initTexture(); - // Deinitializes any existing OpenGL textures, is automatically called in destructor. + // Deinitializes all glyph atlas textures. void deinitTexture(); }; @@ -217,7 +212,7 @@ private: // Shape text using HarfBuzz. void shapeText(const std::string& text, std::vector& segmentsHB); - // Completely recreate the texture data for all textures based on mGlyphs information. + // Completely recreate the texture data for all glyph atlas entries. void rebuildTextures(); void unloadTextures(); @@ -256,12 +251,7 @@ private: int mMaxGlyphHeight; }; -// Used to store a sort of "pre-rendered" string. -// When a TextCache is constructed (Font::buildTextCache()), the vertices and texture coordinates -// of the string are calculated and stored in the TextCache object. Rendering a previously -// constructed TextCache (Font::renderTextCache) every frame is much faster than rebuilding -// one every frame. Keep in mind you still need the Font object to render a TextCache (as the -// Font holds the OpenGL texture), and if a Font changes your TextCache may become invalid. +// Caching of shaped and rendered text. class TextCache { public: