#include "FontCacheManager.h" #include #include #include FontCacheManager::FontCacheManager(const std::map& fontMap) : fontMap_(fontMap) {} void FontCacheManager::setFontDecompressor(FontDecompressor* d) { fontDecompressor_ = d; } void FontCacheManager::clearCache() { if (fontDecompressor_) fontDecompressor_->clearCache(); } void FontCacheManager::prewarmCache(int fontId, const char* utf8Text, uint8_t styleMask) { 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); } void FontCacheManager::resetStats() { if (fontDecompressor_) fontDecompressor_->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); }