8std::unordered_map<std::string, std::pair<std::uint32_t, std::uint32_t>> TextRenderer::sScriptRanges{
10 {{
"Latin"}, {0x0020, 0x007E}},
11 {{
"Cyrillic"}, {0x0400, 0x04FF}},
12 {{
"Greek"}, {0x0370, 0x03FF}},
15 {{
"Han"}, {0x4E00, 0x9FFF}},
16 {{
"HanExtensionA"}, {0x3400, 0x4DBF}},
18 {{
"Hiragana"}, {0x3040, 0x309F}},
19 {{
"Katakana"}, {0x30A0, 0x30FF}},
20 {{
"KatakanaPhoneticExtensions"}, {0x31F0, 0x31FF}},
22 {{
"Hangul"}, {0xAC00, 0xD7AF}},
26 {{
"Hebrew"}, {0x0590, 0x05FF}},
27 {{
"Arabic"}, {0x0600, 0x06FF}},
28 {{
"ArabicSupplement"}, {0x0750, 0x077F}},
29 {{
"ArabicExtended-A"}, {0x08A0, 0x08FF}},
30 {{
"Aramaic"}, {0x0700, 0x074F}},
31 {{
"Thaana"}, {0x0780, 0x07BF}},
34 {{
"Devanagari"}, {0x0900, 0x097F}},
35 {{
"Bengali"}, {0x0980, 0x09FF}},
36 {{
"Gurmukhi"}, {0x0A00, 0x0A7F}},
37 {{
"Gujarati"}, {0x0A80, 0x0AFF}},
38 {{
"Oriya"}, {0x0B00, 0x0B7F}},
39 {{
"Tamil"}, {0x0B80, 0x0BFF}},
40 {{
"Telugu"}, {0x0C00, 0x0C7F}},
41 {{
"Kannada"}, {0x0C80, 0x0CFF}},
42 {{
"Malayalam"}, {0x0D00, 0x0D7F}},
43 {{
"Sinhala"}, {0x0D80, 0x0DFF}},
46 {{
"Thai"}, {0x0E00, 0x0E7F}},
47 {{
"Lao"}, {0x0E80, 0x0EFF}},
48 {{
"Myanmar"}, {0x1000, 0x109F}},
49 {{
"Khmer"}, {0x1780, 0x17FF}},
52 {{
"Tibetan"}, {0x0F00, 0x0FFF}}
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 auto key = std::make_pair(fontName, fontSize);
197 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, mAvailableFontsInFolder[fontName], fontSize);
199 return mFontResources[key];
214 bStoreFontAtlasesInFiles = in;
219 return sScriptsRequiresShaping;
224 std::pair<std::uint32_t, std::uint32_t> result{0, 0};
226 auto search = sScriptRanges.find(script);
228 if (search != sScriptRanges.end())
229 return sScriptRanges[script];
236 LOG_INFO(
"Scanning font folder: " + pathToFolder);
238 boost::filesystem::path pathToDirectory = boost::filesystem::path(pathToFolder);
239 const bool isValidFolderPath = boost::filesystem::exists(boost::filesystem::path(pathToFolder)) && boost::filesystem::is_directory(boost::filesystem::path(pathToFolder));
240 if (!isValidFolderPath)
246 for (boost::filesystem::recursive_directory_iterator it(pathToDirectory), end;
250 const boost::filesystem::path& filePath = it->path();
252 if (!boost::filesystem::is_regular_file(filePath))
255 const std::string ext = filePath.extension().string();
256 if (ext !=
".ttf" && ext !=
".otf")
260 std::string fontName = filePath.stem().string();
261 mAvailableFontsInFolder[fontName] = filePath.string();
264 std::to_string(mAvailableFontsInFolder.size()) +
265 " fonts in folder.");
270 LOG_INFO(
"Preloading font atlases from folder...");
271 auto start = std::chrono::steady_clock::now();
274 for (
const auto& [fontName, filePath] : availableFontsInFolder)
278 auto preloadableScriptIt = std::find(sFontAtlasPreloadableScripts.begin(), sFontAtlasPreloadableScripts.end(), requestedScript);
279 if (preloadableScriptIt != sFontAtlasPreloadableScripts.end())
283 auto key = std::make_pair(fontName, fontSize);
284 if (mFontResources.find(key) == mFontResources.end())
286 LOG_DEBUG(
"Creating font resource: " + fontName +
287 " size " + std::to_string(fontSize));
288 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, filePath, fontSize);
289 mFontResources[key]->StoreFontAtlasesInFiles(bStoreFontAtlasesInFiles);
291 LOG_DEBUG(
"Preloading script '" + requestedScript +
292 "' for font " + fontName +
293 " size " + std::to_string(fontSize));
294 const std::uint32_t rangeBegin = sScriptRanges[requestedScript].first;
295 const std::uint32_t rangeEnd = sScriptRanges[requestedScript].second;
296 mFontResources[key]->LoadGlyphsFromCodePointRange(rangeBegin, rangeEnd);
302 auto end = std::chrono::steady_clock::now();
303 float ms = std::chrono::duration<float, std::milli>(end - start).count();
305 LOG_INFO(
"Font atlas preloading (folder) completed in " +
306 std::to_string(ms) +
" ms.");
311 LOG_INFO(
"Scanning fonts in package...");
314 std::string folderEntry = {
"Fonts/" };
315 for (
auto& entry : entries)
317 const std::string& virtualPath = entry.first;
318 if (virtualPath.rfind(folderEntry, 0) == 0)
320 auto fontFilePath = boost::filesystem::path(virtualPath);
321 const std::string ext = fontFilePath.extension().string();
322 if (ext !=
".ttf" && ext !=
".otf")
326 std::string fontName = fontFilePath.stem().string();
327 mAvailableFontsInPackage[fontName] = fontFilePath.string();
331 std::to_string(mAvailableFontsInPackage.size()) +
332 " fonts in package.");
337 LOG_INFO(
"Preloading font atlases from package...");
338 auto start = std::chrono::steady_clock::now();
341 for (
const auto& [fontName, virtualFilePath] : availableFontsInPackage)
345 auto preloadableScriptIt = std::find(sFontAtlasPreloadableScripts.begin(), sFontAtlasPreloadableScripts.end(), requestedScript);
346 if (preloadableScriptIt != sFontAtlasPreloadableScripts.end())
350 auto key = std::make_pair(fontName, fontSize);
351 if (mFontResources.find(key) == mFontResources.end())
354 LOG_DEBUG(
"Creating font resource: " + fontName +
355 " size " + std::to_string(fontSize));
356 mFontResources[key] = std::make_shared<FontResources>(mRenderResourceContext,
this, fontName, binaryFileData, fontSize);
357 mFontResources[key]->StoreFontAtlasesInFiles(bStoreFontAtlasesInFiles);
359 LOG_DEBUG(
"Preloading script '" + requestedScript +
360 "' for font " + fontName +
361 " size " + std::to_string(fontSize));
362 const std::uint32_t rangeBegin = sScriptRanges[requestedScript].first;
363 const std::uint32_t rangeEnd = sScriptRanges[requestedScript].second;
364 mFontResources[key]->LoadGlyphsFromCodePointRange(rangeBegin, rangeEnd);
370 auto end = std::chrono::steady_clock::now();
371 float ms = std::chrono::duration<float, std::milli>(end - start).count();
373 LOG_INFO(
"Font atlas preloading (package) completed in " +
374 std::to_string(ms) +
" ms.");
TextRenderer(RenderResourceContext rrc)
Constructs the text renderer and initializes internal font systems.
void LoadFontsAvailableInFolder(std::string pathToFolder)
Discovers fonts available in a filesystem folder.
std::shared_ptr< FontResources > GetFontResources(const std::string &fontName, int fontSize)
Retrieves or creates font resources for a font and size.
~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< std::string > textScripts
Unicode scripts to preload for text rendering.
std::vector< int > fontSizePreload
Font sizes to preload at startup.
Aggregates pointers to global rendering resource managers.