diff --git a/Aerofoil/GpFileSystem_Win32.cpp b/Aerofoil/GpFileSystem_Win32.cpp index 2b4915e..8393261 100644 --- a/Aerofoil/GpFileSystem_Win32.cpp +++ b/Aerofoil/GpFileSystem_Win32.cpp @@ -123,18 +123,21 @@ GpFileSystem_Win32::GpFileSystem_Win32() m_userSavesDir = m_prefsDir + L"\\SavedGames"; m_scoresDir = m_prefsDir + L"\\Scores"; m_logsDir = m_prefsDir + L"\\Logs"; + m_fontCacheDir = m_prefsDir + L"\\FontCache"; CreateDirectoryW(m_prefsDir.c_str(), nullptr); CreateDirectoryW(m_scoresDir.c_str(), nullptr); CreateDirectoryW(m_userHousesDir.c_str(), nullptr); CreateDirectoryW(m_userSavesDir.c_str(), nullptr); CreateDirectoryW(m_logsDir.c_str(), nullptr); + CreateDirectoryW(m_fontCacheDir.c_str(), nullptr); m_prefsDir.append(L"\\"); m_scoresDir.append(L"\\"); m_userHousesDir.append(L"\\"); m_userSavesDir.append(L"\\"); m_logsDir.append(L"\\"); + m_fontCacheDir.append(L"\\"); } DWORD modulePathSize = GetModuleFileNameW(nullptr, m_executablePath, MAX_PATH); @@ -462,6 +465,9 @@ bool GpFileSystem_Win32::ResolvePath(PortabilityLayer::VirtualDirectory_t virtua case PortabilityLayer::VirtualDirectories::kLogs: baseDir = m_logsDir.c_str(); break; + case PortabilityLayer::VirtualDirectories::kFontCache: + baseDir = m_fontCacheDir.c_str(); + break; default: return false; } diff --git a/Aerofoil/GpFileSystem_Win32.h b/Aerofoil/GpFileSystem_Win32.h index 7a8cf25..2fdc9f2 100644 --- a/Aerofoil/GpFileSystem_Win32.h +++ b/Aerofoil/GpFileSystem_Win32.h @@ -41,6 +41,7 @@ private: std::wstring m_userHousesDir; std::wstring m_userSavesDir; std::wstring m_resourcesDir; + std::wstring m_fontCacheDir; wchar_t m_executablePath[MAX_PATH]; static GpFileSystem_Win32 ms_instance; diff --git a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp index b52c1ab..dd09b46 100644 --- a/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpFileSystem_Android.cpp @@ -441,6 +441,9 @@ bool GpFileSystem_Android::ResolvePath(PortabilityLayer::VirtualDirectory_t virt case PortabilityLayer::VirtualDirectories::kPrefs: prefsAppend = "Prefs"; break; + case PortabilityLayer::VirtualDirectories::kFontCache: + prefsAppend = "FontCache"; + break; default: return false; }; @@ -509,7 +512,7 @@ void GpFileSystem_Android::InitJNI() prefsDir[i] = '/'; } } - const char *extensions[] = { "HighScores", "Houses", "SavedGames", "Prefs" }; + const char *extensions[] = { "HighScores", "Houses", "SavedGames", "Prefs", "FontCache" }; for (size_t i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { std::string prefsPath = std::string(prefsDir) + extensions[i]; diff --git a/PortabilityLayer/FontFamily.cpp b/PortabilityLayer/FontFamily.cpp index a30c763..36d9a8f 100644 --- a/PortabilityLayer/FontFamily.cpp +++ b/PortabilityLayer/FontFamily.cpp @@ -60,13 +60,18 @@ namespace PortabilityLayer return m_hacks[variation]; } - FontFamily *FontFamily::Create() + int FontFamily::GetCacheID() const + { + return m_cacheID; + } + + FontFamily *FontFamily::Create(int cacheID) { void *storage = malloc(sizeof(FontFamily)); if (!storage) return nullptr; - return new (storage) FontFamily(); + return new (storage) FontFamily(cacheID); } void FontFamily::Destroy() @@ -75,8 +80,9 @@ namespace PortabilityLayer free(this); } - FontFamily::FontFamily() + FontFamily::FontFamily(int cacheID) : m_defaultVariation(0) + , m_cacheID(cacheID) { for (unsigned int i = 0; i < kNumVariations; i++) { diff --git a/PortabilityLayer/FontFamily.h b/PortabilityLayer/FontFamily.h index fe5209c..6c7a25c 100644 --- a/PortabilityLayer/FontFamily.h +++ b/PortabilityLayer/FontFamily.h @@ -29,15 +29,18 @@ namespace PortabilityLayer IGpFont *GetFontForVariation(int variation) const; FontHacks GetHacksForVariation(int variation) const; - static FontFamily *Create(); + int GetCacheID() const; + + static FontFamily *Create(int cacheID); void Destroy(); private: FontHacks m_hacks[kNumVariations]; IGpFont *m_fonts[kNumVariations]; uint8_t m_defaultVariation; + int m_cacheID; - FontFamily(); + explicit FontFamily(int cacheID); ~FontFamily(); }; } diff --git a/PortabilityLayer/FontManager.cpp b/PortabilityLayer/FontManager.cpp index f929ecc..939ca8f 100644 --- a/PortabilityLayer/FontManager.cpp +++ b/PortabilityLayer/FontManager.cpp @@ -7,7 +7,9 @@ #include "HostFontHandler.h" #include "GpIOStream.h" #include "RenderedFont.h" +#include "PLBigEndian.h" +#include #include #include @@ -27,10 +29,17 @@ namespace PortabilityLayer RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) override; RenderedFont *GetRenderedFontFromFamily(FontFamily *font, int size, bool aa, int flags) override; + RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const override; + void SaveCachedRenderedFont(const RenderedFont *rfont, int cacheID, int size, bool aa, int flags) const override; + static FontManagerImpl *GetInstance(); private: static const unsigned int kNumCachedRenderedFonts = 32; + static const int kSystemFontCacheID = 1; + static const int kApplicationFontCacheID = 2; + static const int kFontCacheVersion = 1; + static const int kFontCacheNameSize = 64; struct CachedRenderedFont { @@ -46,6 +55,8 @@ namespace PortabilityLayer void ResetUsageCounter(); static int CRFSortPredicate(const void *a, const void *b); + static void GenerateCacheFileName(char(&str)[kFontCacheNameSize], int cacheID, int size, bool aa, int flags); + FontFamily *m_systemFont; FontFamily *m_applicationFont; uint32_t m_usageCounter; @@ -57,8 +68,8 @@ namespace PortabilityLayer void FontManagerImpl::Init() { - m_systemFont = FontFamily::Create(); - m_applicationFont = FontFamily::Create(); + m_systemFont = FontFamily::Create(kSystemFontCacheID); + m_applicationFont = FontFamily::Create(kApplicationFontCacheID); if (m_systemFont) m_systemFont->AddFont(FontFamilyFlag_None, "Fonts/OpenSans/OpenSans-ExtraBold.ttf", FontHacks_None); @@ -162,13 +173,71 @@ namespace PortabilityLayer RenderedFont *FontManagerImpl::GetRenderedFontFromFamily(FontFamily *fontFamily, int size, bool aa, int flags) { + PortabilityLayer::FontManager *fm = PortabilityLayer::FontManager::GetInstance(); + + RenderedFont *rfont = fm->LoadCachedRenderedFont(fontFamily->GetCacheID(), size, aa, flags); + if (rfont) + return rfont; + const int variation = fontFamily->GetVariationForFlags(flags); IGpFont *hostFont = fontFamily->GetFontForVariation(variation); if (!hostFont) return nullptr; - return PortabilityLayer::FontManager::GetInstance()->GetRenderedFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation)); + rfont = fm->GetRenderedFont(hostFont, size, aa, fontFamily->GetHacksForVariation(variation)); + if (rfont) + fm->SaveCachedRenderedFont(rfont, fontFamily->GetCacheID(), size, aa, flags); + + return rfont; + } + + RenderedFont *FontManagerImpl::LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const + { + char filename[kFontCacheNameSize]; + GenerateCacheFileName(filename, cacheID, size, aa, flags); + + GpIOStream *stream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFile(PortabilityLayer::VirtualDirectories::kFontCache, filename, false, GpFileCreationDispositions::kOpenExisting); + if (!stream) + return nullptr; + + BEUInt32_t version; + if (stream->Read(&version, sizeof(version)) != sizeof(version) || version != kFontCacheVersion) + { + stream->Close(); + return nullptr; + } + + RenderedFont *rfont = PortabilityLayer::FontRenderer::GetInstance()->LoadCache(stream); + stream->Close(); + + return rfont; + } + + void FontManagerImpl::SaveCachedRenderedFont(const RenderedFont *rfont, int cacheID, int size, bool aa, int flags) const + { + char filename[kFontCacheNameSize]; + GenerateCacheFileName(filename, cacheID, size, aa, flags); + + GpIOStream *stream = PortabilityLayer::HostFileSystem::GetInstance()->OpenFile(PortabilityLayer::VirtualDirectories::kFontCache, filename, true, GpFileCreationDispositions::kCreateOrOverwrite); + if (!stream) + return; + + BEUInt32_t zero32(0); + if (stream->Write(&zero32, sizeof(zero32)) != sizeof(zero32)) + { + stream->Close(); + return; + } + + if (PortabilityLayer::FontRenderer::GetInstance()->SaveCache(rfont, stream)) + { + BEUInt32_t version(kFontCacheVersion); + stream->SeekStart(0); + stream->Write(&version, sizeof(version)); + } + + stream->Close(); } FontManagerImpl *FontManagerImpl::GetInstance() @@ -222,6 +291,11 @@ namespace PortabilityLayer return 0; } + void FontManagerImpl::GenerateCacheFileName(char(&str)[kFontCacheNameSize], int cacheID, int size, bool aa, int flags) + { + sprintf(str, "rf_%i_%i_%s_%i.cache", cacheID, size, aa ? "aa" : "mc", flags); + } + FontManagerImpl FontManagerImpl::ms_instance; FontManager *FontManager::GetInstance() diff --git a/PortabilityLayer/FontManager.h b/PortabilityLayer/FontManager.h index 5e76714..0692679 100644 --- a/PortabilityLayer/FontManager.h +++ b/PortabilityLayer/FontManager.h @@ -21,6 +21,9 @@ namespace PortabilityLayer virtual RenderedFont *GetRenderedFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) = 0; virtual RenderedFont *GetRenderedFontFromFamily(FontFamily *fontFamily, int fontSize, bool aa, int flags) = 0; + virtual RenderedFont *LoadCachedRenderedFont(int cacheID, int size, bool aa, int flags) const = 0; + virtual void SaveCachedRenderedFont(const RenderedFont *rfont, int cacheID, int size, bool aa, int flags) const = 0; + static FontManager *GetInstance(); }; } diff --git a/PortabilityLayer/FontRenderer.cpp b/PortabilityLayer/FontRenderer.cpp index f118404..b6bd455 100644 --- a/PortabilityLayer/FontRenderer.cpp +++ b/PortabilityLayer/FontRenderer.cpp @@ -2,9 +2,11 @@ #include "CoreDefs.h" #include "IGpFont.h" +#include "GpIOStream.h" #include "HostFontHandler.h" #include "IGpFontRenderedGlyph.h" #include "MacRomanConversion.h" +#include "PLBigEndian.h" #include "PLPasStr.h" #include "RenderedFont.h" #include "GpRenderedFontMetrics.h" @@ -30,12 +32,28 @@ namespace PortabilityLayer void SetCharData(unsigned int charID, const void *data, size_t dataOffset, const GpRenderedGlyphMetrics &metrics); void SetFontMetrics(const GpRenderedFontMetrics &metrics); + static RenderedFont *Load(GpIOStream *stream); + bool Save(GpIOStream *stream) const; + static RenderedFontImpl *Create(size_t glyphDataSize, bool aa); private: - RenderedFontImpl(void *data, bool aa); + struct CacheHeader + { + BEUInt32_t m_cacheVersion; + BEUInt32_t m_glyphDataSize; + BEUInt32_t m_sizeSize; + + BEUInt32_t m_isAA; + }; + + static const uint32_t kRFontCacheVersion = 2; + + RenderedFontImpl(void *data, size_t dataSize, bool aa); ~RenderedFontImpl(); + bool LoadInternal(GpIOStream *stream); + size_t m_dataOffsets[256]; GpRenderedGlyphMetrics m_glyphMetrics[256]; @@ -43,12 +61,15 @@ namespace PortabilityLayer bool m_isAntiAliased; void *m_data; + size_t m_dataSize; }; class FontRendererImpl final : public FontRenderer { public: RenderedFont *RenderFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) override; + RenderedFont *LoadCache(GpIOStream *stream) override; + bool SaveCache(const RenderedFont *rfont, GpIOStream *stream) override; static FontRendererImpl *GetInstance(); @@ -109,6 +130,57 @@ namespace PortabilityLayer m_fontMetrics = metrics; } + RenderedFont *RenderedFontImpl::Load(GpIOStream *stream) + { + CacheHeader header; + if (stream->Read(&header, sizeof(header)) != sizeof(header)) + return nullptr; + + if (header.m_cacheVersion != kRFontCacheVersion) + return nullptr; + + if (header.m_sizeSize != sizeof(size_t)) + return nullptr; + + RenderedFontImpl *rfont = RenderedFontImpl::Create(header.m_glyphDataSize, header.m_isAA != 0); + if (!rfont) + return nullptr; + + if (!rfont->LoadInternal(stream)) + { + rfont->Destroy(); + return nullptr; + } + + return rfont; + } + + bool RenderedFontImpl::Save(GpIOStream *stream) const + { + CacheHeader header; + header.m_cacheVersion = kRFontCacheVersion; + header.m_glyphDataSize = this->m_dataSize; + header.m_isAA = m_isAntiAliased; + header.m_sizeSize = sizeof(size_t); + + if (stream->Write(&header, sizeof(header)) != sizeof(header)) + return false; + + if (stream->Write(m_data, m_dataSize) != m_dataSize) + return false; + + if (stream->Write(m_dataOffsets, sizeof(m_dataOffsets)) != sizeof(m_dataOffsets)) + return false; + + if (stream->Write(m_glyphMetrics, sizeof(m_glyphMetrics)) != sizeof(m_glyphMetrics)) + return false; + + if (stream->Write(&m_fontMetrics, sizeof(m_fontMetrics)) != sizeof(m_fontMetrics)) + return false; + + return true; + } + RenderedFontImpl *RenderedFontImpl::Create(size_t glyphDataSize, bool aa) { size_t alignedPrefixSize = sizeof(RenderedFontImpl) + GP_SYSTEM_MEMORY_ALIGNMENT - 1; @@ -125,11 +197,12 @@ namespace PortabilityLayer memset(storage, 0, allocSize); - return new (storage) RenderedFontImpl(static_cast(storage) + alignedPrefixSize, aa); + return new (storage) RenderedFontImpl(static_cast(storage) + alignedPrefixSize, glyphDataSize, aa); } - RenderedFontImpl::RenderedFontImpl(void *data, bool aa) + RenderedFontImpl::RenderedFontImpl(void *data, size_t dataSize, bool aa) : m_data(data) + , m_dataSize(dataSize) , m_isAntiAliased(aa) { memset(m_glyphMetrics, 0, sizeof(m_glyphMetrics)); @@ -141,6 +214,24 @@ namespace PortabilityLayer { } + bool RenderedFontImpl::LoadInternal(GpIOStream *stream) + { + if (stream->Read(m_data, m_dataSize) != m_dataSize) + return false; + + if (stream->Read(m_dataOffsets, sizeof(m_dataOffsets)) != sizeof(m_dataOffsets)) + return false; + + if (stream->Read(m_glyphMetrics, sizeof(m_glyphMetrics)) != sizeof(m_glyphMetrics)) + return false; + + if (stream->Read(&m_fontMetrics, sizeof(m_fontMetrics)) != sizeof(m_fontMetrics)) + return false; + + return true; + } + + RenderedFont *FontRendererImpl::RenderFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) { const unsigned int numCharacters = 256; @@ -262,6 +353,19 @@ namespace PortabilityLayer return rfont; } + RenderedFont *FontRendererImpl::LoadCache(GpIOStream *stream) + { + return RenderedFontImpl::Load(stream); + } + + bool FontRendererImpl::SaveCache(const RenderedFont *rfont, GpIOStream *stream) + { + if (!static_cast(rfont)->Save(stream)) + return false; + + return true; + } + FontRendererImpl *FontRendererImpl::GetInstance() { return &ms_instance; diff --git a/PortabilityLayer/FontRenderer.h b/PortabilityLayer/FontRenderer.h index 130226d..2447cf5 100644 --- a/PortabilityLayer/FontRenderer.h +++ b/PortabilityLayer/FontRenderer.h @@ -3,6 +3,7 @@ #include "FontHacks.h" struct IGpFont; +class GpIOStream; namespace PortabilityLayer { @@ -12,6 +13,8 @@ namespace PortabilityLayer { public: virtual RenderedFont *RenderFont(IGpFont *font, int size, bool aa, FontHacks fontHacks) = 0; + virtual RenderedFont *LoadCache(GpIOStream *stream) = 0; + virtual bool SaveCache(const RenderedFont *rfont, GpIOStream *stream) = 0; static FontRenderer *GetInstance(); }; diff --git a/PortabilityLayer/VirtualDirectory.h b/PortabilityLayer/VirtualDirectory.h index 1f426ee..a8447b6 100644 --- a/PortabilityLayer/VirtualDirectory.h +++ b/PortabilityLayer/VirtualDirectory.h @@ -17,6 +17,7 @@ namespace PortabilityLayer kCursors, kHighScores, kLogs, + kFontCache, kSourceExport, };