#include "FontCacheManager.h" #include #include #include #include FontCacheManager::FontCacheManager(const std::map& fontMap, const std::map& sdCardFonts) : fontMap_(fontMap), sdCardFonts_(sdCardFonts) {} void FontCacheManager::setFontDecompressor(FontDecompressor* d) { fontDecompressor_ = d; } void FontCacheManager::clearCache() { if (fontDecompressor_) fontDecompressor_->clearCache(); for (auto& [id, font] : sdCardFonts_) { font->clearCache(); } } void FontCacheManager::prewarmCache(int fontId, const char* utf8Text, uint8_t styleMask) { // SD card font prewarm path: prewarm all requested styles in one call auto sdIt = sdCardFonts_.find(fontId); if (sdIt != sdCardFonts_.end()) { int missed = sdIt->second->prewarm(utf8Text, styleMask); if (missed > 0) { LOG_DBG("FCM", "prewarmCache(SD): %d glyph(s) not found (styleMask=0x%02X)", missed, styleMask); } return; } // Standard compressed font prewarm path: loop over all requested styles if (!fontDecompressor_ || fontMap_.count(fontId) == 0) return; for (uint8_t i = 0; i < 4; i++) { if (!(styleMask & (1 << i))) continue; auto style = static_cast(i); const EpdFontData* data = fontMap_.at(fontId).getData(style); if (!data || !data->groups) continue; int missed = fontDecompressor_->prewarmCache(data, utf8Text); if (missed > 0) { LOG_DBG("FCM", "prewarmCache: %d glyph(s) not cached for style %d", missed, i); } } } void FontCacheManager::logStats(const char* label) { if (fontDecompressor_) fontDecompressor_->logStats(label); for (auto& [id, font] : sdCardFonts_) { font->logStats(label); } } void FontCacheManager::resetStats() { if (fontDecompressor_) fontDecompressor_->resetStats(); for (auto& [id, font] : sdCardFonts_) { font->resetStats(); } } bool FontCacheManager::isScanning() const { return scanMode_ == ScanMode::Scanning; } void FontCacheManager::recordText(const char* text, int fontId, EpdFontFamily::Style style) { scanText_ += text; if (scanFontId_ < 0) scanFontId_ = fontId; const uint8_t baseStyle = static_cast(style) & 0x03; const unsigned char* p = reinterpret_cast(text); uint32_t cpCount = 0; while (*p) { if ((*p & 0xC0) != 0x80) cpCount++; p++; } scanStyleCounts_[baseStyle] += cpCount; } // --- PrewarmScope implementation --- FontCacheManager::PrewarmScope::PrewarmScope(FontCacheManager& manager) : manager_(&manager) { manager_->scanMode_ = ScanMode::Scanning; manager_->clearCache(); manager_->resetStats(); manager_->scanText_.clear(); manager_->scanText_.reserve(2048); // Pre-allocate to avoid heap fragmentation from repeated concat memset(manager_->scanStyleCounts_, 0, sizeof(manager_->scanStyleCounts_)); manager_->scanFontId_ = -1; } void FontCacheManager::PrewarmScope::endScanAndPrewarm() { manager_->scanMode_ = ScanMode::None; if (manager_->scanText_.empty()) return; // Build style bitmask from all styles that appeared during the scan uint8_t styleMask = 0; for (uint8_t i = 0; i < 4; i++) { if (manager_->scanStyleCounts_[i] > 0) styleMask |= (1 << i); } if (styleMask == 0) styleMask = 1; // default to regular manager_->prewarmCache(manager_->scanFontId_, manager_->scanText_.c_str(), styleMask); // Free scan string memory manager_->scanText_.clear(); manager_->scanText_.shrink_to_fit(); } FontCacheManager::PrewarmScope::~PrewarmScope() { if (active_) { endScanAndPrewarm(); // no-op if already called (scanText_ is empty) manager_->clearCache(); } } FontCacheManager::PrewarmScope::PrewarmScope(PrewarmScope&& other) noexcept : manager_(other.manager_), active_(other.active_) { other.active_ = false; } FontCacheManager::PrewarmScope FontCacheManager::createPrewarmScope() { return PrewarmScope(*this); }