108 lines
3.4 KiB
C++
108 lines
3.4 KiB
C++
#include "SdCardFontManager.h"
|
||
|
||
#include <EpdFontFamily.h>
|
||
#include <GfxRenderer.h>
|
||
#include <Logging.h>
|
||
#include <SdCardFont.h>
|
||
#include <SdCardFontRegistry.h>
|
||
|
||
#include <cstdlib>
|
||
|
||
SdCardFontManager::~SdCardFontManager() {
|
||
if (renderer_) {
|
||
unloadAll(*renderer_);
|
||
} else {
|
||
for (auto& lf : loaded_) {
|
||
delete lf.font;
|
||
}
|
||
loaded_.clear();
|
||
}
|
||
}
|
||
|
||
// 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<uint8_t>(*familyName++);
|
||
hash *= FNV_PRIME;
|
||
}
|
||
hash ^= pointSize;
|
||
hash *= FNV_PRIME;
|
||
int id = static_cast<int>(hash);
|
||
return id != 0 ? id : 1; // 0 is reserved as "not found" sentinel
|
||
}
|
||
|
||
bool SdCardFontManager::loadFamily(const SdCardFontFamilyInfo& family, GfxRenderer& renderer, uint8_t targetPtSize) {
|
||
if (!renderer_) {
|
||
renderer_ = &renderer;
|
||
}
|
||
// Unload any previously loaded family first
|
||
if (!loadedFamilyName_.empty()) {
|
||
unloadAll(renderer);
|
||
}
|
||
|
||
// Pick the single file whose size is closest to targetPtSize. Loading
|
||
// only one size bounds resident memory (intervals + kern/ligature tables
|
||
// per style) to one file's worth, vs. N_sizes × per-file overhead.
|
||
const SdCardFontFileInfo* selected = family.pickClosestSize(targetPtSize);
|
||
if (!selected) {
|
||
LOG_ERR("SDMGR", "Family %s has no files to load", family.name.c_str());
|
||
return false;
|
||
}
|
||
|
||
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 (target=%u)", selected->path.c_str(), selected->pointSize, fontId,
|
||
font->styleCount(), targetPtSize);
|
||
|
||
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) {
|
||
if (!renderer_) {
|
||
renderer_ = &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;
|
||
}
|