From aead8e726423eed8fd3843a0796411f99472afa2 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 2 Oct 2023 00:10:57 -0500 Subject: [PATCH] Start of dynamic ImGui font loading --- .../source/scwx/qt/manager/font_manager.cpp | 106 +++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 1cc752fe..994d5a53 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -2,10 +2,13 @@ #include #include +#include #include #include #include +#include +#include #include namespace scwx @@ -27,6 +30,18 @@ struct FontRecord std::string filename_ {}; }; +template +struct FontRecordHash; + +template +struct FontRecordKeyEqual; + +template<> +struct FontRecordHash> +{ + size_t operator()(const std::pair& x) const; +}; + class FontManager::Impl { public: @@ -41,12 +56,24 @@ public: void InitializeFontCache(); void InitializeFontconfig(); + const std::vector& GetRawFontData(const std::string& filename); + static FontRecord MatchFontFile(const std::string& family, const std::vector& styles); std::string fontCachePath_ {}; std::shared_mutex imguiFontAtlasMutex_ {}; + + boost::unordered_flat_map, + std::shared_ptr, + FontRecordHash>> + imguiFonts_ {}; + std::shared_mutex imguiFontsMutex_ {}; + + boost::unordered_flat_map> + rawFontData_ {}; + std::mutex rawFontDataMutex_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} @@ -71,12 +98,69 @@ FontManager::GetImGuiFont(const std::string& family, FontRecord fontRecord = Impl::MatchFontFile(family, styles); - // TODO: - Q_UNUSED(fontRecord); + // Only allow whole pixels, and clamp to 6-72 pt + units::font_size::pixels pixels {size}; + int imFontSize = std::clamp(static_cast(pixels.value()), 8, 96); + + // Search for a loaded ImGui font + { + std::shared_lock imguiFontLock {p->imguiFontsMutex_}; + + // Search for the associated ImGui font + auto it = p->imguiFonts_.find(std::make_pair(fontRecord, imFontSize)); + if (it != p->imguiFonts_.end()) + { + return it->second; + } + + // No ImGui font was found, we need to create one + } + + // Get raw font data + const auto& rawFontData = p->GetRawFontData(fontRecord.filename_); + + // TODO: Create an ImGui font + // TODO: imguiFontLock could be acquired during a render loop, when the font + // atlas is already locked. Lock the font atlas first. Unless it's already + // locked. It might need to be reentrant? + // TODO: Search for font once more, to prevent loading the same font twice + Q_UNUSED(rawFontData); return nullptr; } +const std::vector& +FontManager::Impl::GetRawFontData(const std::string& filename) +{ + std::unique_lock rawFontDataLock {rawFontDataMutex_}; + + auto it = rawFontData_.find(filename); + if (it != rawFontData_.end()) + { + // Raw font data has already been loaded + return it->second; + } + + // Raw font data needs to be loaded + std::basic_ifstream ifs {filename, std::ios::binary}; + ifs.seekg(0, std::ios_base::end); + std::size_t dataSize = ifs.tellg(); + ifs.seekg(0, std::ios_base::beg); + + // Store the font data in a buffer + std::vector buffer {}; + buffer.reserve(dataSize); + std::copy(std::istreambuf_iterator(ifs), + std::istreambuf_iterator(), + std::back_inserter(buffer)); + + // Place the buffer in the cache + auto result = rawFontData_.emplace(filename, std::move(buffer)); + + // Return the cached buffer + return it->second; +} + void FontManager::LoadApplicationFont(const std::string& filename) { // If the font cache failed to create, don't attempt to cache any fonts @@ -227,6 +311,24 @@ FontManager& FontManager::Instance() return instance_; } +size_t FontRecordHash>::operator()( + const std::pair& x) const +{ + size_t seed = 0; + boost::hash_combine(seed, x.first.family_); + boost::hash_combine(seed, x.first.style_); + boost::hash_combine(seed, x.first.filename_); + boost::hash_combine(seed, x.second); + return seed; +} + +bool operator==(const FontRecord& lhs, const FontRecord& rhs) +{ + return lhs.family_ == rhs.family_ && // + lhs.style_ == rhs.style_ && // + lhs.filename_ == rhs.filename_; +} + } // namespace manager } // namespace qt } // namespace scwx