#include "SdCardFontManager.h" #include #include #include #include #include SdCardFontManager::~SdCardFontManager() { for (auto& lf : loaded_) { delete lf.font; } } // FNV-1a continuation: seeds with contentHash, then hashes family name + point size. // Produces a deterministic ID that is stable across load/unload cycles and reboots, // and changes when font content changes (different header/TOC = different contentHash). int SdCardFontManager::computeFontId(uint32_t contentHash, const char* familyName, uint8_t pointSize) { static constexpr uint32_t FNV_PRIME = 16777619u; uint32_t hash = contentHash; while (*familyName) { hash ^= static_cast(*familyName++); hash *= FNV_PRIME; } hash ^= pointSize; hash *= FNV_PRIME; int id = static_cast(hash); return id != 0 ? id : 1; // 0 is reserved as "not found" sentinel } bool SdCardFontManager::loadFamily(const SdCardFontFamilyInfo& family, GfxRenderer& renderer, uint8_t fontSizeEnum) { // Unload any previously loaded family first if (!loadedFamilyName_.empty()) { unloadAll(renderer); } // Select by ordinal position: sort available sizes, then map the font size // enum (SMALL=0 .. EXTRA_LARGE=3) to the corresponding slot. When the // family has fewer sizes than 4, clamp to the last available size. auto sizes = family.availableSizes(); if (sizes.empty()) { LOG_ERR("SDMGR", "Family %s has no files to load", family.name.c_str()); return false; } uint8_t idx = fontSizeEnum; if (idx >= sizes.size()) idx = sizes.size() - 1; const SdCardFontFileInfo* selected = family.findFile(sizes[idx]); auto* font = new (std::nothrow) SdCardFont(); if (!font) { LOG_ERR("SDMGR", "Failed to allocate SdCardFont for %s", selected->path.c_str()); return false; } if (!font->load(selected->path.c_str())) { LOG_ERR("SDMGR", "Failed to load %s", selected->path.c_str()); delete font; return false; } int fontId = computeFontId(font->contentHash(), family.name.c_str(), selected->pointSize); // Guard against collision with built-in font IDs (astronomically unlikely // with FNV-1a hashes, but provides a safety net) if (renderer.getFontMap().count(fontId) != 0) { LOG_ERR("SDMGR", "Font ID %d collides with existing font, skipping %s", fontId, selected->path.c_str()); delete font; return false; } renderer.registerSdCardFont(fontId, font); loaded_.push_back({font, fontId, selected->pointSize}); LOG_DBG("SDMGR", "Loaded %s size=%u id=%d styles=%u (sizeEnum=%u)", selected->path.c_str(), selected->pointSize, fontId, font->styleCount(), fontSizeEnum); EpdFontFamily fontFamily(font->getEpdFont(0), font->getEpdFont(1), font->getEpdFont(2), font->getEpdFont(3)); renderer.insertFont(fontId, fontFamily); loadedFamilyName_ = family.name; loadedPointSize_ = selected->pointSize; return true; } void SdCardFontManager::unloadAll(GfxRenderer& renderer) { renderer.clearSdCardFonts(); for (auto& lf : loaded_) { renderer.removeFont(lf.fontId); delete lf.font; } loaded_.clear(); loadedFamilyName_.clear(); loadedPointSize_ = 0; } int SdCardFontManager::getFontId(const std::string& familyName) const { if (familyName != loadedFamilyName_ || loaded_.empty()) return 0; return loaded_.front().fontId; }