mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-26 08:05:38 +00:00
Some font-related code and comments cleanup
This commit is contained in:
parent
03f6567dd5
commit
a7d673f541
|
@ -3,8 +3,7 @@
|
||||||
// ES-DE Frontend
|
// ES-DE Frontend
|
||||||
// Font.h
|
// Font.h
|
||||||
//
|
//
|
||||||
// Loading, unloading, caching, shaping and rendering of fonts.
|
// Font management and text shaping and rendering.
|
||||||
// Also functions for text wrapping and similar.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
|
@ -654,7 +653,7 @@ Font::FontTexture::FontTexture(const int mFontSize)
|
||||||
rowHeight = 0;
|
rowHeight = 0;
|
||||||
writePos = glm::ivec2 {1, 1};
|
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.
|
// more textures will be created dynamically.
|
||||||
textureSize = glm::ivec2 {mFontSize * 6, mFontSize * 6};
|
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()
|
void Font::FontTexture::initTexture()
|
||||||
{
|
{
|
||||||
assert(textureId == 0);
|
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
|
// glyphs will not be visible. That would otherwise lead to edge artifacts as these pixels
|
||||||
// would get sampled during scaling.
|
// would get sampled during scaling.
|
||||||
std::vector<uint8_t> texture(textureSize.x * textureSize.y * 4, 0);
|
std::vector<uint8_t> 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
|
// 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()
|
// 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<FT_F26Dot6>(0.0f), static_cast<FT_F26Dot6>(size * 64.0f), 0,
|
FT_Set_Char_Size(face, static_cast<FT_F26Dot6>(0.0f), static_cast<FT_F26Dot6>(size * 64.0f), 0,
|
||||||
0);
|
0);
|
||||||
fontHB = fontArg;
|
fontHB = fontArg;
|
||||||
|
@ -867,6 +866,7 @@ void Font::shapeText(const std::string& text, std::vector<ShapeSegment>& segment
|
||||||
else {
|
else {
|
||||||
// This also advances the cursor.
|
// This also advances the cursor.
|
||||||
character = Utils::String::chars2Unicode(segment.substring, cursor);
|
character = Utils::String::chars2Unicode(segment.substring, cursor);
|
||||||
|
|
||||||
Glyph* glyph {getGlyph(character)};
|
Glyph* glyph {getGlyph(character)};
|
||||||
segment.shapedWidth += glyph->advance.x;
|
segment.shapedWidth += glyph->advance.x;
|
||||||
segment.glyphIndexes.emplace_back(std::make_pair(character, 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<ShapeSegment>& segment
|
||||||
|
|
||||||
void Font::rebuildTextures()
|
void Font::rebuildTextures()
|
||||||
{
|
{
|
||||||
// Recreate OpenGL textures.
|
// Recreate all glyph atlas textures.
|
||||||
for (auto it = mTextures.begin(); it != mTextures.end(); ++it)
|
for (auto it = mTextures.begin(); it != mTextures.end(); ++it)
|
||||||
(*it)->initTexture();
|
(*it)->initTexture();
|
||||||
|
|
||||||
|
@ -909,7 +909,6 @@ void Font::rebuildTextures()
|
||||||
getFaceForGlyphIndex(std::get<0>(it->first), std::get<1>(it->first), &returnedFont)};
|
getFaceForGlyphIndex(std::get<0>(it->first), std::get<1>(it->first), &returnedFont)};
|
||||||
FT_GlyphSlot glyphSlot {(*face)->glyph};
|
FT_GlyphSlot glyphSlot {(*face)->glyph};
|
||||||
|
|
||||||
// Load the glyph bitmap through FreeType.
|
|
||||||
FT_Load_Glyph(*face, std::get<0>(it->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};
|
||||||
|
@ -917,7 +916,6 @@ void Font::rebuildTextures()
|
||||||
static_cast<int>(it->second.texPos.x * it->second.texture->textureSize.x),
|
static_cast<int>(it->second.texPos.x * it->second.texture->textureSize.x),
|
||||||
static_cast<int>(it->second.texPos.y * it->second.texture->textureSize.y)};
|
static_cast<int>(it->second.texPos.y * it->second.texture->textureSize.y)};
|
||||||
|
|
||||||
// Upload glyph bitmap to texture.
|
|
||||||
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
||||||
mRenderer->updateTexture(it->second.texture->textureId, 0, Renderer::TextureType::RED,
|
mRenderer->updateTexture(it->second.texture->textureId, 0, Renderer::TextureType::RED,
|
||||||
cursor.x, cursor.y, glyphSize.x, glyphSize.y,
|
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;
|
*returnedFont = nullptr;
|
||||||
return &mFontFace->face;
|
return &mFontFace->face;
|
||||||
}
|
}
|
||||||
|
@ -1019,16 +1016,7 @@ Font::Glyph* Font::getGlyph(const unsigned int id)
|
||||||
|
|
||||||
const FT_GlyphSlot glyphSlot {(*face)->glyph};
|
const FT_GlyphSlot glyphSlot {(*face)->glyph};
|
||||||
|
|
||||||
// If the font does not contain hinting information then force the use of the automatic
|
if (FT_Load_Char(*face, id, FT_LOAD_RENDER)) {
|
||||||
// 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<bool>(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))) {
|
|
||||||
LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath
|
LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath
|
||||||
<< ", size " << mFontSize;
|
<< ", size " << mFontSize;
|
||||||
return nullptr;
|
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.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6};
|
||||||
glyph.rows = glyphSize.y;
|
glyph.rows = glyphSize.y;
|
||||||
|
|
||||||
// Upload glyph bitmap to texture.
|
// Upload glyph bitmap to glyph atlas texture.
|
||||||
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
||||||
mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
|
mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
|
||||||
glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
|
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.
|
// We need to create a new entry.
|
||||||
FT_Face* face {getFaceForGlyphIndex(id, fontArg, &returnedFont)};
|
FT_Face* face {getFaceForGlyphIndex(id, fontArg, &returnedFont)};
|
||||||
if (!face) {
|
if (!face) {
|
||||||
LOG(LogError) << "Couldn't find appropriate font face for character " << id << " for font "
|
LOG(LogError) << "Couldn't find appropriate font face for glyph index " << id
|
||||||
<< mPath;
|
<< " for font " << mPath;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FT_GlyphSlot glyphSlot {(*face)->glyph};
|
const FT_GlyphSlot glyphSlot {(*face)->glyph};
|
||||||
|
|
||||||
// If the font does not contain hinting information then force the use of the automatic
|
if (FT_Load_Glyph(*face, id, FT_LOAD_RENDER)) {
|
||||||
// hinter that is built into FreeType. Note: Using font-supplied hints generally looks worse
|
LOG(LogError) << "Couldn't find glyph for glyph index " << id << " for font " << mPath
|
||||||
// than using the auto-hinter so it's disabled for now.
|
|
||||||
// const bool hasHinting {static_cast<bool>(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
|
|
||||||
<< ", size " << mFontSize;
|
<< ", size " << mFontSize;
|
||||||
return nullptr;
|
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.
|
// This should (hopefully) never occur as size constraints are enforced earlier on.
|
||||||
if (tex == nullptr) {
|
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)";
|
<< ", size " << mFontSize << " (no suitable texture found)";
|
||||||
return nullptr;
|
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.bearing = {glyphSlot->metrics.horiBearingX >> 6, glyphSlot->metrics.horiBearingY >> 6};
|
||||||
glyph.rows = glyphSize.y;
|
glyph.rows = glyphSize.y;
|
||||||
|
|
||||||
// Upload glyph bitmap to texture.
|
// Upload glyph bitmap to glyph atlas texture.
|
||||||
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
if (glyphSize.x > 0 && glyphSize.y > 0) {
|
||||||
mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
|
mRenderer->updateTexture(tex->textureId, 0, Renderer::TextureType::RED, cursor.x, cursor.y,
|
||||||
glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
|
glyphSize.x, glyphSize.y, glyphSlot->bitmap.buffer);
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
// ES-DE Frontend
|
// ES-DE Frontend
|
||||||
// Font.h
|
// Font.h
|
||||||
//
|
//
|
||||||
// Loading, unloading, caching, shaping and rendering of fonts.
|
// Font management and text shaping and rendering.
|
||||||
// Also functions for text wrapping and similar.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ES_CORE_RESOURCES_FONT_H
|
#ifndef ES_CORE_RESOURCES_FONT_H
|
||||||
|
@ -33,8 +32,6 @@ class TextCache;
|
||||||
#define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf"
|
#define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf"
|
||||||
#define FONT_PATH_BOLD ":/fonts/Akrobat-Bold.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
|
class Font : public IReloadable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -135,9 +132,9 @@ public:
|
||||||
const float sizeMultiplier = 1.0f,
|
const float sizeMultiplier = 1.0f,
|
||||||
const bool fontSizeDimmed = false);
|
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;
|
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();
|
static size_t getTotalMemUsage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -155,11 +152,9 @@ private:
|
||||||
bool findEmpty(const glm::ivec2& size, glm::ivec2& cursorOut);
|
bool findEmpty(const glm::ivec2& size, glm::ivec2& cursorOut);
|
||||||
|
|
||||||
// You must call initTexture() after creating a FontTexture to get a textureId.
|
// 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();
|
void initTexture();
|
||||||
|
|
||||||
// Deinitializes any existing OpenGL textures, is automatically called in destructor.
|
// Deinitializes all glyph atlas textures.
|
||||||
void deinitTexture();
|
void deinitTexture();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,7 +212,7 @@ private:
|
||||||
// Shape text using HarfBuzz.
|
// Shape text using HarfBuzz.
|
||||||
void shapeText(const std::string& text, std::vector<ShapeSegment>& segmentsHB);
|
void shapeText(const std::string& text, std::vector<ShapeSegment>& 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 rebuildTextures();
|
||||||
void unloadTextures();
|
void unloadTextures();
|
||||||
|
|
||||||
|
@ -256,12 +251,7 @@ private:
|
||||||
int mMaxGlyphHeight;
|
int mMaxGlyphHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to store a sort of "pre-rendered" string.
|
// Caching of shaped and rendered text.
|
||||||
// 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.
|
|
||||||
class TextCache
|
class TextCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in a new issue