8std::unordered_map<std::string, std::pair<std::uint32_t, std::uint32_t>> TextRenderer::sScriptRanges{
10 {{
"Latin"}, {0x0020U, 0x007EU}},
11 {{
"Cyrillic"}, {0x0400U, 0x04FFU}},
12 {{
"Greek"}, {0x0370U, 0x03FFU}},
15 {{
"Han"}, {0x4E00U, 0x9FFFU}},
16 {{
"HanExtensionA"}, {0x3400U, 0x4DBFU}},
18 {{
"Hiragana"}, {0x3040U, 0x309FU}},
19 {{
"Katakana"}, {0x30A0U, 0x30FFU}},
20 {{
"KatakanaPhoneticExtensions"}, {0x31F0U, 0x31FFU}},
22 {{
"Hangul"}, {0xAC00U, 0xD7AFU}},
26 {{
"Hebrew"}, {0x0590U, 0x05FFU}},
27 {{
"Arabic"}, {0x0600U, 0x06FFU}},
28 {{
"ArabicSupplement"}, {0x0750U, 0x077FU}},
29 {{
"ArabicExtended-A"}, {0x08A0U, 0x08FFU}},
30 {{
"Aramaic"}, {0x0700U, 0x074FU}},
31 {{
"Thaana"}, {0x0780U, 0x07BFU}},
34 {{
"Devanagari"}, {0x0900U, 0x097FU}},
35 {{
"Bengali"}, {0x0980U, 0x09FFU}},
36 {{
"Gurmukhi"}, {0x0A00U, 0x0A7FU}},
37 {{
"Gujarati"}, {0x0A80U, 0x0AFFU}},
38 {{
"Oriya"}, {0x0B00U, 0x0B7FU}},
39 {{
"Tamil"}, {0x0B80U, 0x0BFFU}},
40 {{
"Telugu"}, {0x0C00U, 0x0C7FU}},
41 {{
"Kannada"}, {0x0C80U, 0x0CFFU}},
42 {{
"Malayalam"}, {0x0D00U, 0x0D7FU}},
43 {{
"Sinhala"}, {0x0D80U, 0x0DFFU}},
46 {{
"Thai"}, {0x0E00U, 0x0E7FU}},
47 {{
"Lao"}, {0x0E80U, 0x0EFFU}},
48 {{
"Myanmar"}, {0x1000U, 0x109FU}},
49 {{
"Khmer"}, {0x1780U, 0x17FFU}},
52 {{
"Tibetan"}, {0x0F00U, 0x0FFFU}}
54std::vector<std::string> TextRenderer::sFontAtlasPreloadableScripts{
66 {
"KatakanaPhoneticExtensions"},
71std::vector<std::string> TextRenderer::sScriptsRequiresShaping{
97 mRenderResourceContext(rrc)
99 LOG_INFO(
"Initializing TextRenderer...");
100 auto start = std::chrono::steady_clock::now();
102 mErrorResult = FT_Init_FreeType(&mLibrary);
105 LOG_ERROR(
"Failed to initialize FreeType library!");
106 throw std::runtime_error{
"Failed to initialize FreeType library!" };
109 auto end = std::chrono::steady_clock::now();
110 float ms = std::chrono::duration<float, std::milli>(end - start).count();
112 LOG_INFO(
"FreeType initialized in " + std::to_string(ms) +
" ms.");
121 LOG_INFO(
"Shutting down TextRenderer...");
125 for (
auto& fontResource : mFontResources)
126 fontResource.second.reset();
128 mFontResources.clear();
130 FT_Done_FreeType(mLibrary);
132 LOG_INFO(
"TextRenderer shutdown complete.");
137 LOG_INFO(
"Loading fonts from folder: " + pathToFolder);
138 auto start = std::chrono::steady_clock::now();
141 auto end = std::chrono::steady_clock::now();
142 float ms = std::chrono::duration<float, std::milli>(end - start).count();
144 LOG_INFO(
"Font discovery and preload complete in " +
145 std::to_string(ms) +
" ms.");
156 return mRenderResourceContext;
161 auto key = std::make_pair(fontName, fontSize);
162 if (
auto search = mFontResources.find(key); search != mFontResources.end())
164 return search->second;
168 if (!mAvailableFontsInPackage.empty())
170 if (!mAvailableFontsInPackage.empty())
172 auto foundInPackage = mAvailableFontsInPackage.find(fontName);
173 if (foundInPackage != mAvailableFontsInPackage.end())
175 const std::string virtualFilePath = mAvailableFontsInPackage[fontName];
178 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, fontName, binaryFileData, fontSize);
179 mFontResources[key]->StoreFontAtlasesInFiles(bStoreFontAtlasesInFiles);
181 return mFontResources[key];
191 if (!mAvailableFontsInFolder.empty())
193 auto foundInFolder = mAvailableFontsInFolder.find(fontName);
194 if (foundInFolder != mAvailableFontsInFolder.end())
196 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, mAvailableFontsInFolder[fontName], fontSize);
198 return mFontResources[key];
213 bStoreFontAtlasesInFiles = in;
218 return sScriptsRequiresShaping;
223 std::pair<std::uint32_t, std::uint32_t> result{ 0U, 0U };
225 auto search = sScriptRanges.find(script);
227 if (search != sScriptRanges.end())
228 return sScriptRanges[script];
235 LOG_INFO(
"Scanning font folder: " + pathToFolder);
237 std::filesystem::path pathToDirectory = std::filesystem::path(pathToFolder);
238 const bool isValidFolderPath = std::filesystem::exists(std::filesystem::path(pathToFolder)) && std::filesystem::is_directory(std::filesystem::path(pathToFolder));
239 if (!isValidFolderPath)
245 for (std::filesystem::recursive_directory_iterator it(pathToDirectory), end;
249 const std::filesystem::path& filePath = it->path();
251 if (!std::filesystem::is_regular_file(filePath))
254 const std::string ext = filePath.extension().string();
255 if (ext !=
".ttf" && ext !=
".otf")
259 std::string fontName = filePath.stem().string();
260 mAvailableFontsInFolder[fontName] = filePath.string();
263 std::to_string(mAvailableFontsInFolder.size()) +
264 " fonts in folder.");
269 LOG_INFO(
"Preloading font atlases from folder...");
270 auto start = std::chrono::steady_clock::now();
273 for (
const auto& [fontName, filePath] : availableFontsInFolder)
277 auto preloadableScriptIt = std::find(sFontAtlasPreloadableScripts.begin(), sFontAtlasPreloadableScripts.end(), requestedScript);
278 if (preloadableScriptIt != sFontAtlasPreloadableScripts.end())
282 auto key = std::make_pair(fontName, fontSize);
283 if (mFontResources.find(key) == mFontResources.end())
285 LOG_DEBUG(
"Creating font resource: " + fontName +
286 " size " + std::to_string(fontSize));
287 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, filePath, fontSize);
288 mFontResources[key]->StoreFontAtlasesInFiles(bStoreFontAtlasesInFiles);
290 LOG_DEBUG(
"Preloading script '" + requestedScript +
291 "' for font " + fontName +
292 " size " + std::to_string(fontSize));
293 const std::uint32_t rangeBegin = sScriptRanges[requestedScript].first;
294 const std::uint32_t rangeEnd = sScriptRanges[requestedScript].second;
295 mFontResources[key]->LoadGlyphsFromCodePointRange(rangeBegin, rangeEnd);
301 auto end = std::chrono::steady_clock::now();
302 float ms = std::chrono::duration<float, std::milli>(end - start).count();
304 LOG_INFO(
"Font atlas preloading (folder) completed in " +
305 std::to_string(ms) +
" ms.");
310 LOG_INFO(
"Scanning fonts in package...");
313 std::string folderEntry = {
"Fonts/" };
314 for (
auto& entry : entries)
316 const std::string& virtualPath = entry.first;
317 if (virtualPath.rfind(folderEntry, 0) == 0)
319 auto fontFilePath = std::filesystem::path(virtualPath);
320 const std::string ext = fontFilePath.extension().string();
321 if (ext !=
".ttf" && ext !=
".otf")
325 std::string fontName = fontFilePath.stem().string();
326 mAvailableFontsInPackage[fontName] = fontFilePath.string();
330 std::to_string(mAvailableFontsInPackage.size()) +
331 " fonts in package.");
336 LOG_INFO(
"Preloading font atlases from package...");
337 auto start = std::chrono::steady_clock::now();
340 for (
const auto& [fontName, virtualFilePath] : availableFontsInPackage)
344 auto preloadableScriptIt = std::find(sFontAtlasPreloadableScripts.begin(), sFontAtlasPreloadableScripts.end(), requestedScript);
345 if (preloadableScriptIt != sFontAtlasPreloadableScripts.end())
349 auto key = std::make_pair(fontName, fontSize);
350 if (mFontResources.find(key) == mFontResources.end())
353 LOG_DEBUG(
"Creating font resource: " + fontName +
354 " size " + std::to_string(fontSize));
355 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, fontName, binaryFileData, fontSize);
356 mFontResources[key]->StoreFontAtlasesInFiles(bStoreFontAtlasesInFiles);
358 LOG_DEBUG(
"Preloading script '" + requestedScript +
359 "' for font " + fontName +
360 " size " + std::to_string(fontSize));
361 const std::uint32_t rangeBegin = sScriptRanges[requestedScript].first;
362 const std::uint32_t rangeEnd = sScriptRanges[requestedScript].second;
363 mFontResources[key]->LoadGlyphsFromCodePointRange(rangeBegin, rangeEnd);
369 auto end = std::chrono::steady_clock::now();
370 float ms = std::chrono::duration<float, std::milli>(end - start).count();
372 LOG_INFO(
"Font atlas preloading (package) completed in " +
373 std::to_string(ms) +
" ms.");
std::shared_ptr< FontResources > GetFontResources(const std::string &fontName, unsigned int fontSize)
Retrieves or creates font resources for a font and size.
TextRenderer(RenderResourceContext rrc)
Constructs the text renderer and initializes internal font systems.
void LoadFontsAvailableInFolder(std::string pathToFolder)
Discovers fonts available in a filesystem folder.
~TextRenderer()
Shuts down the text system and releases font backend resources.
void StoreFontAtlasesInFiles(bool in)
Enables or disables storing generated font atlases to files.
const RenderResourceContext & GetRenderResourceContext() const
Returns the rendering resource context.
void LoadFontsFromFolder(std::string pathToFolder)
Loads fonts from a filesystem folder.
void LoadFontsAvailableInPackage()
Discovers fonts available in the application package.
void LoadPreloadableFontAtlasesFromFolder(const std::unordered_map< std::string, std::string > &availableFontsInFolder)
Loads preloadable font atlases from a folder.
void LoadFontsFromPackage()
Loads fonts bundled inside an application package.
const std::vector< std::string > & GetScriptsRequiredShaping() const
Returns scripts that require shaping before rendering.
void LoadPreloadableFontAtlasesFromPackage(const std::unordered_map< std::string, std::string > &availableFontsInPackage)
Loads preloadable font atlases from the application package.
std::pair< std::uint32_t, std::uint32_t > GetScriptRange(std::string script)
Returns the Unicode codepoint range for a script.
static const PackEntries & GetPackEntries()
Returns the manifest of packed files.
static AppConfig ReadConfigFile()
Reads application settings from the JSON config file.
static std::vector< uint8_t > ReadPackedFile(const std::string &entryPath)
Reads raw bytes of a file stored inside Pack.bin.
Engine-wide logging system for runtime diagnostics and performance tracking.
Basic application settings loaded from a configuration file.
std::vector< unsigned int > fontSizePreload
Font sizes to preload at startup.
std::vector< std::string > textScripts
Unicode scripts to preload for text rendering.
Aggregates pointers to global rendering resource managers.