#include "FontManager.h" #include "FontFamily.h" #include "FontRenderer.h" #include "FileManager.h" #include "GpIOStream.h" #include "IGpFileSystem.h" #include "IGpFont.h" #include "IGpSystemServices.h" #include "MemReaderStream.h" #include "PLBigEndian.h" #include "PLDrivers.h" #include "RenderedFontCatalog.h" #include "RenderedFont.h" #include "ResTypeID.h" #include "ResourceManager.h" #include #include #include void PL_NotYetImplemented(); namespace PortabilityLayer { class FontManagerImpl final : public FontManager { public: void Init() override; void Shutdown() override; FontFamily *GetFont(FontFamilyID_t fontFamilyID) const override; void GetFontPreset(FontPreset_t fontPreset, FontFamilyID_t *outFamilyID, int *outSize, int *outVariationFlags, bool *outAA) const override; RenderedFont *LoadCachedRenderedFont(FontFamilyID_t familyID, int size, bool aa, int flags) override; void PurgeCache() override; static FontManagerImpl *GetInstance(); private: RenderedFont *LoadAndRenderFontUsingFontHandler(FontFamily *font, int size, bool aa, int flags); RenderedFont *LoadAndRenderFont(FontFamilyID_t familyID, int size, bool aa, int flags); static const unsigned int kNumCachedRenderedFonts = 32; struct CachedRenderedFont { RenderedFont *m_rfont; int m_fontCacheID; int m_size; int m_flags; bool m_aa; uint32_t m_lastUsage; }; struct FontPreset { FontFamilyID_t m_familyID; int m_textSize; int m_variationFlags; bool m_aa; }; FontManagerImpl(); bool FindOrReserveCacheSlot(int cacheID, int size, bool aa, int flags, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF); void ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags); void ResetUsageCounter(); static int CRFSortPredicate(const void *a, const void *b); FontFamily *m_fontFamilies[FontFamilyIDs::kCount]; uint32_t m_usageCounter; CachedRenderedFont m_cachedRenderedFonts[kNumCachedRenderedFonts]; IResourceArchive *m_fontArchive; PortabilityLayer::CompositeFile *m_fontArchiveFile; THandle m_fontArchiveCatalogData; bool m_hasPreinstalledFonts; static FontManagerImpl ms_instance; static FontPreset ms_fontPresets[FontPresets::kCount]; }; void FontManagerImpl::Init() { for (int i = 0; i < FontFamilyIDs::kCount; i++) m_fontFamilies[static_cast(i)] = FontFamily::Create(static_cast(i)); if (m_fontFamilies[FontFamilyIDs::kSystemSymbols]) m_fontFamilies[FontFamilyIDs::kSystemSymbols]->AddFont(FontFamilyFlag_None, VirtualDirectories::kFonts, "Fonts/Inter/Inter-SemiBold.ttf", 0, FontHacks_SystemSymbols); if (m_fontFamilies[FontFamilyIDs::kSystem]) m_fontFamilies[FontFamilyIDs::kSystem]->AddFont(FontFamilyFlag_None, VirtualDirectories::kFonts, "Fonts/OpenSans/OpenSans-ExtraBold.ttf", 0, FontHacks_None); if (m_fontFamilies[FontFamilyIDs::kApplication]) { m_fontFamilies[FontFamilyIDs::kApplication]->AddFont(FontFamilyFlag_None, VirtualDirectories::kFonts, "Fonts/OpenSans/OpenSans-SemiBold.ttf", 0, FontHacks_None); m_fontFamilies[FontFamilyIDs::kApplication]->AddFont(FontFamilyFlag_Bold, VirtualDirectories::kFonts, "Fonts/OpenSans/OpenSans-Bold.ttf", 0, FontHacks_None); m_fontFamilies[FontFamilyIDs::kApplication]->AddFont(FontFamilyFlag_SyntheticBold, VirtualDirectories::kFonts, "Fonts/OpenSans/OpenSans-Regular.ttf", 0, FontHacks_SyntheticBold_OpenSans); } if (m_fontFamilies[FontFamilyIDs::kHandwriting]) m_fontFamilies[FontFamilyIDs::kHandwriting]->AddFont(FontFamilyFlag_None, VirtualDirectories::kFonts, "Fonts/GochiHand/GochiHand-Regular.ttf", 0, FontHacks_None); if (m_fontFamilies[FontFamilyIDs::kMonospace]) m_fontFamilies[FontFamilyIDs::kMonospace]->AddFont(FontFamilyFlag_None, VirtualDirectories::kFonts, "Fonts/Roboto/RobotoMono-Regular.ttf", 0, FontHacks_None); memset(m_cachedRenderedFonts, 0, sizeof(m_cachedRenderedFonts)); } void FontManagerImpl::Shutdown() { for (int fid = 0; fid < FontFamilyIDs::kCount; fid++) { if (m_fontFamilies[fid]) m_fontFamilies[fid]->Destroy(); } IGpFontHandler *hfh = PLDrivers::GetFontHandler(); for (int i = 0; i < sizeof(m_cachedRenderedFonts) / sizeof(m_cachedRenderedFonts[0]); i++) { CachedRenderedFont *crf = m_cachedRenderedFonts + i; if (crf->m_rfont) crf->m_rfont->Destroy(); } } FontFamily *FontManagerImpl::GetFont(FontFamilyID_t fontFamilyID) const { return m_fontFamilies[fontFamilyID]; } void FontManagerImpl::GetFontPreset(FontPreset_t preset, FontFamilyID_t *outFamilyID, int *outSize, int *outVariationFlags, bool *outAA) const { if (outSize) *outSize = ms_fontPresets[preset].m_textSize; if (outVariationFlags) *outVariationFlags = ms_fontPresets[preset].m_variationFlags; if (outAA) *outAA = ms_fontPresets[preset].m_aa; if (outFamilyID) *outFamilyID = ms_fontPresets[preset].m_familyID; } bool FontManagerImpl::FindOrReserveCacheSlot(int cacheID, int size, bool aa, int flags, CachedRenderedFont *&outCacheSlot, RenderedFont *&outRF) { CachedRenderedFont *newCacheSlot = &m_cachedRenderedFonts[0]; for (int i = 0; i < kNumCachedRenderedFonts; i++) { CachedRenderedFont &crf = m_cachedRenderedFonts[i]; if (crf.m_rfont == nullptr) { newCacheSlot = &crf; break; } if (crf.m_fontCacheID == cacheID && crf.m_size == size && crf.m_aa == aa && crf.m_flags == flags) { crf.m_lastUsage = m_usageCounter; RenderedFont *rf = crf.m_rfont; if (m_usageCounter == UINT32_MAX) ResetUsageCounter(); else m_usageCounter++; outRF = rf; return true; } if (newCacheSlot->m_rfont != nullptr && crf.m_lastUsage < newCacheSlot->m_lastUsage) newCacheSlot = &crf; } outCacheSlot = newCacheSlot; return false; } void FontManagerImpl::ReplaceCachedRenderedFont(CachedRenderedFont &cacheSlot, RenderedFont *rfont, int cacheID, int size, bool aa, int flags) { if (cacheSlot.m_rfont) cacheSlot.m_rfont->Destroy(); cacheSlot.m_fontCacheID = cacheID; cacheSlot.m_lastUsage = m_usageCounter; cacheSlot.m_size = size; cacheSlot.m_rfont = rfont; cacheSlot.m_aa = aa; cacheSlot.m_flags = flags; if (m_usageCounter == UINT32_MAX) ResetUsageCounter(); else m_usageCounter++; } RenderedFont *FontManagerImpl::LoadAndRenderFontUsingFontHandler(FontFamily *fontFamily, int size, bool aa, int flags) { PortabilityLayer::FontManager *fm = PortabilityLayer::FontManager::GetInstance(); const int variation = fontFamily->GetVariationForFlags(flags); IGpFont *hostFont = fontFamily->GetFontForVariation(variation); if (!hostFont) return nullptr; RenderedFont *rfont = FontRenderer::GetInstance()->RenderFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation)); fontFamily->UnloadVariation(variation); return rfont; } RenderedFont *FontManagerImpl::LoadCachedRenderedFont(FontFamilyID_t familyID, int size, bool aa, int flags) { CachedRenderedFont *cacheSlot = nullptr; RenderedFont *rfont = nullptr; if (this->FindOrReserveCacheSlot(familyID, size, aa, flags, cacheSlot, rfont)) return rfont; rfont = LoadAndRenderFont(familyID, size, aa, flags); if (rfont) ReplaceCachedRenderedFont(*cacheSlot, rfont, familyID, size, aa, flags); return rfont; } RenderedFont *FontManagerImpl::LoadAndRenderFont(FontFamilyID_t familyID, int size, bool aa, int flags) { FontFamily *fontFamily = this->GetFont(familyID); RenderedFont *rfont = nullptr; if (PLDrivers::GetFontHandler() != nullptr) { rfont = LoadAndRenderFontUsingFontHandler(fontFamily, size, aa, flags); if (rfont != nullptr) return rfont; } if (!m_fontArchive) { m_fontArchiveFile = PortabilityLayer::FileManager::GetInstance()->OpenCompositeFile(VirtualDirectories::kApplicationData, PSTR("Fonts")); if (!m_fontArchiveFile) return nullptr; m_fontArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(m_fontArchiveFile); if (!m_fontArchive) { m_fontArchiveFile->Close(); return nullptr; } m_fontArchiveCatalogData = m_fontArchive->LoadResource('RFCT', 1000); if (!m_fontArchiveCatalogData) { m_fontArchive->Destroy(); m_fontArchiveFile->Close(); return nullptr; } } int variation = fontFamily->GetVariationForFlags(flags); FontHacks hacks = FontHacks_None; VirtualDirectory_t vDir = VirtualDirectories::kUnspecified; const char *path = nullptr; int typeFaceIndex = 0; if (!fontFamily->GetFontSpec(variation, hacks, vDir, path, typeFaceIndex)) return nullptr; // Only single TTF fonts are supported by the prerendered font system currently if (vDir != VirtualDirectories::kFonts || typeFaceIndex != 0) return nullptr; size_t pathLen = strlen(path); // Parse font cache header const uint8_t *catalogBytes = static_cast(*m_fontArchiveCatalogData); RenderedFontCatalogHeader catHeader; memcpy(&catHeader, catalogBytes, sizeof(catHeader)); if (catHeader.m_version != RenderedFontCatalogHeader::kVersion) return nullptr; size_t numPaths = catHeader.m_numPaths; size_t numFonts = catHeader.m_numRFonts; const uint8_t *pathsBytes = catalogBytes + catHeader.m_pathsOffset; const uint8_t *pathEntryBytes = catalogBytes + sizeof(RenderedFontCatalogHeader); const uint8_t *fontEntryBytes = pathEntryBytes + sizeof(RenderedFontCatalogPathEntry) * numPaths; size_t pathIndex = numPaths; for (size_t i = 0; i < numPaths; i++) { RenderedFontCatalogPathEntry pathEntry; memcpy(&pathEntry, pathEntryBytes + i * sizeof(RenderedFontCatalogPathEntry), sizeof(RenderedFontCatalogPathEntry)); if (pathEntry.m_pathSize != pathLen) continue; if (!memcmp(pathsBytes + pathEntry.m_pathOffset, path, pathLen)) { pathIndex = i; break; } } if (pathIndex == numPaths) return nullptr; size_t fontIndex = numFonts; for (size_t i = 0; i < numFonts; i++) { RenderedFontCatalogRFontEntry fontEntry; memcpy(&fontEntry, fontEntryBytes + i * sizeof(RenderedFontCatalogRFontEntry), sizeof(RenderedFontCatalogRFontEntry)); if (fontEntry.m_pathIndex == pathIndex && static_cast(fontEntry.m_hacks) == hacks && fontEntry.m_fontSize == size && (fontEntry.m_isAA != 0) == aa) { fontIndex = i; break; } } if (fontIndex == numFonts) return nullptr; THandle res = m_fontArchive->LoadResource('RFNT', 1000 + static_cast(fontIndex)); if (res) { PortabilityLayer::MemReaderStream stream(*res, res.MMBlock()->m_size); rfont = PortabilityLayer::FontRenderer::GetInstance()->LoadCache(&stream); res.Dispose(); } return rfont; } void FontManagerImpl::PurgeCache() { for (int fid = 0; fid < FontFamilyIDs::kCount; fid++) { if (m_fontFamilies[fid]) m_fontFamilies[fid]->PurgeCache(); } } FontManagerImpl *FontManagerImpl::GetInstance() { return &ms_instance; } FontManagerImpl::FontManagerImpl() : m_fontArchive(nullptr) , m_fontArchiveFile(nullptr) , m_hasPreinstalledFonts(false) { for (int fid = 0; fid < FontFamilyIDs::kCount; fid++) m_fontFamilies[fid] = nullptr; } void FontManagerImpl::ResetUsageCounter() { // Resets the usage counter if it would overflow by sorting by ascending last usage and then resetting all counts to simple +1 increments qsort(m_cachedRenderedFonts, kNumCachedRenderedFonts, sizeof(CachedRenderedFont), FontManagerImpl::CRFSortPredicate); m_usageCounter = 0; for (unsigned int i = 0; i < kNumCachedRenderedFonts; i++) { CachedRenderedFont &crf = m_cachedRenderedFonts[i]; if (!crf.m_rfont) break; crf.m_lastUsage = m_usageCounter++; } } int FontManagerImpl::CRFSortPredicate(const void *a, const void *b) { const CachedRenderedFont *crfA = static_cast(a); const CachedRenderedFont *crfB = static_cast(b); if (crfA->m_rfont == nullptr && crfB->m_rfont == nullptr) return 0; if (crfA->m_rfont == nullptr && crfB->m_rfont != nullptr) return 1; if (crfA->m_rfont != nullptr && crfB->m_rfont == nullptr) return -1; if (crfA->m_lastUsage < crfB->m_lastUsage) return -1; if (crfA->m_lastUsage > crfB->m_lastUsage) return 1; return 0; } FontManagerImpl::FontPreset FontManagerImpl::ms_fontPresets[FontPresets::kCount] = { { FontFamilyIDs::kSystemSymbols, 12, FontFamilyFlag_None, true }, { FontFamilyIDs::kSystem, 12, FontFamilyFlag_None, true }, { FontFamilyIDs::kSystem, 12, FontFamilyFlag_Bold, true }, { FontFamilyIDs::kApplication, 8, FontFamilyFlag_None, true }, { FontFamilyIDs::kApplication, 9, FontFamilyFlag_None, true }, { FontFamilyIDs::kApplication, 9, FontFamilyFlag_SyntheticBold, true }, { FontFamilyIDs::kApplication, 10, FontFamilyFlag_SyntheticBold, true }, { FontFamilyIDs::kApplication, 12, FontFamilyFlag_Bold, true }, { FontFamilyIDs::kApplication, 12, FontFamilyFlag_SyntheticBold, true }, { FontFamilyIDs::kApplication, 14, FontFamilyFlag_None, true }, { FontFamilyIDs::kApplication, 14, FontFamilyFlag_SyntheticBold, true }, { FontFamilyIDs::kApplication, 18, FontFamilyFlag_None, true }, { FontFamilyIDs::kApplication, 40, FontFamilyFlag_None, true }, { FontFamilyIDs::kMonospace, 10, FontFamilyFlag_None, true }, { FontFamilyIDs::kHandwriting, 24, FontFamilyFlag_None, true }, { FontFamilyIDs::kHandwriting, 48, FontFamilyFlag_None, true }, }; FontManagerImpl FontManagerImpl::ms_instance; FontManager *FontManager::GetInstance() { return FontManagerImpl::GetInstance(); } }