Skip to main content

FreeTypeFontResourceManager.ixx File

FreeType-based font loading and glyph caching for OpenGL text rendering. More...

Included Headers

#include <string_view> #include <cassert> #include <ft2build.h> #include <glad/gl.h> #include <ostream> #include <ranges> #include <unordered_map> #include <helios.engine.modules.ui.widgets.types.FontId> #include <helios.rendering.text.Glyph> #include <helios.rendering.text.FontResourceProvider>

Namespaces Index

namespacehelios
namespaceext

Platform-specific extensions and backend implementations. More...

namespaceopengl

OpenGL-specific implementations. More...

namespacerendering

OpenGL rendering implementations. More...

Classes Index

classFreeTypeFontResourceManager

FreeType-based implementation of `FontResourceProvider` for OpenGL. More...

structFontCache

Internal cache structure for a single font's glyphs. More...

Description

FreeType-based font loading and glyph caching for OpenGL text rendering.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file FreeTypeFontResourceManager.ixx
3 * @brief FreeType-based font loading and glyph caching for OpenGL text rendering.
4 */
5module;
6
7#include <string_view>
8
9#include <cassert>
10#include <ft2build.h>
11#include <glad/gl.h>
12
13#include <ostream>
14#include <ranges>
15#include <unordered_map>
16
17
18#include FT_FREETYPE_H
19
20export module helios.ext.opengl.rendering.FreeTypeFontResourceManager;
21
22import helios.rendering.text.FontResourceProvider;
23import helios.rendering.text.Glyph;
24
25import helios.engine.modules.ui.widgets.types.FontId;
26
27
29
30 /**
31 * @brief FreeType-based implementation of `FontResourceProvider` for OpenGL.
32 *
33 * `FreeTypeFontResourceManager` uses the FreeType library to load TrueType fonts,
34 * rasterize glyphs, and cache them as OpenGL textures. Each glyph is stored as a
35 * separate texture for simplicity.
36 *
37 * ## Responsibilities
38 *
39 * - **Font Loading:** Parse font files using FreeType and extract glyph metrics.
40 * - **Texture Creation:** Upload glyph bitmaps to GPU as GL_R8 textures.
41 * - **Caching:** Store glyph data per font for efficient repeated access.
42 * - **Resource Cleanup:** Delete OpenGL textures when the manager is destroyed.
43 *
44 * ## Usage
45 *
46 * ```cpp
47 * FreeTypeFontResourceManager fontManager;
48 * fontManager.loadFont(FontId{"roboto"}, "fonts/Roboto-Regular.ttf");
49 *
50 * // Retrieve a glyph for rendering
51 * Glyph g = fontManager.glyph('A', FontId{"roboto"});
52 * ```
53 *
54 * @note Currently loads ASCII characters 0-127 with a fixed pixel size of 24
55 *
56 * @see FontResourceProvider
57 * @see Glyph
58 * @see OpenGLGlyphTextRenderer
59 */
61
62 /**
63 * @brief Internal cache structure for a single font's glyphs.
64 */
65 struct FontCache {
66 /**
67 * @brief Array of cached glyphs, indexed by character code offset.
68 */
69 helios::rendering::text::Glyph characters[128];
70 };
71
72 /**
73 * @brief Map of font IDs to their cached glyph data.
74 */
75 std::unordered_map<
77 > fontCache_;
78
79
80 public:
81
82 /**
83 * @brief Destructor that cleans up all OpenGL textures.
84 *
85 * Iterates through all cached fonts and deletes the associated glyph textures
86 * to free GPU memory.
87 */
89
90 // Cleanup texture memory for all loaded fonts
91 for (const auto& cache: fontCache_ | std::views::values) {
92 for (const auto& glyph : cache.characters) {
93 // glDeleteTextures ignores 0, so this is safe even if textureId is uninitialized (0)
94 glDeleteTextures(1, &glyph.textureId);
95 }
96 }
97 }
98
99 /**
100 * @brief Loads a font from a file and caches its glyphs as OpenGL textures.
101 *
102 * Uses FreeType to parse the font file and rasterize ASCII characters 0-127.
103 * Each glyph bitmap is uploaded to the GPU as a GL_R8 texture.
104 *
105 * @param fontId Unique identifier for this font.
106 * @param pixelHeight The height of the font, in pixels.
107 * @param pathToFont File system path to the font file (e.g., `.ttf`).
108 *
109 * @throws std::runtime_error If FreeType initialization fails.
110 * @throws std::runtime_error If the font file cannot be loaded.
111 * @throws std::runtime_error If a glyph cannot be loaded.
112 *
113 * @pre The font ID must not already be loaded (asserts in debug builds).
114 */
117 const unsigned int pixelHeight,
118 const std::string_view pathToFont
119 ) override {
120
121 const int begin = 0;
122 const int end = 128;
123
124 assert(!fontCache_.contains(fontId) && "FontCache already existing");
125
126 fontCache_.emplace(fontId, FontCache{});
127
128 FT_Library ft{};
129
130 FT_Face face{};
131
132 if (FT_Init_FreeType(&ft)) {
133 throw std::runtime_error("ERROR::FREETYPE: Could not init FreeType Library");
134
135 }
136
137 if (FT_New_Face(ft, pathToFont.data(), 0, &face)) {
138 throw std::runtime_error("ERROR::FREETYPE: Failed to load font");
139 }
140
141 FT_Set_Pixel_Sizes(face, 0, pixelHeight);
142 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
143 //glPixelStorei(GL_UNPACK_ROW_LENGTH, face->glyph->bitmap.pitch);
144
145 for (unsigned short c = begin; c < end; ++c) {
146
147 if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
148 throw std::runtime_error("ERROR::FREETYTPE: Failed to load Glyph");
149 }
150
151 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
152
153 unsigned int texture;
154 glGenTextures(1, &texture);
155 glBindTexture(GL_TEXTURE_2D, texture);
156 glTexImage2D(
157 GL_TEXTURE_2D,
158 0,
159 GL_R8,
160 face->glyph->bitmap.width,
161 face->glyph->bitmap.rows,
162 0,
163 GL_RED,
164 GL_UNSIGNED_BYTE,
165 face->glyph->bitmap.buffer
166 );
167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
168 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
171 glGenerateMipmap(GL_TEXTURE_2D);
172
173 fontCache_[fontId].characters[c - begin] = {
174 texture,
175 {face->glyph->bitmap.width, face->glyph->bitmap.rows},
176 {face->glyph->bitmap_left, face->glyph->bitmap_top},
177 face->glyph->advance.x
178 };
179
180 }
181
182 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
183 FT_Done_Face(face);
184 FT_Done_FreeType(ft);
185 };
186
187 /**
188 * @brief Retrieves the cached glyph data for a character.
189 *
190 * Returns the glyph metrics and texture ID for the specified character
191 * from the font's cache. The font must have been previously loaded via `loadFont()`.
192 *
193 * @param c The character to retrieve the glyph for.
194 * @param fontId The font identifier to use.
195 *
196 * @return The cached `Glyph` data for the character.
197 *
198 * @pre The font must already be loaded (asserts in debug builds).
199 */
201 const char c,
203 ) override {
204
205 assert(fontCache_.contains(fontId) && "Font cache entry not found for given FontId (font not loaded?)");
206
207 return fontCache_[fontId].characters[c];
208 }
209
210
211
212 };
213
214
215}

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.