Add SdCardFontFamilyInfo::pickClosestSize(targetPtSize) and have the
manager and SdCardFontSystem drive size selection from a target point
size derived from the user's font-size enum (SMALL=12, MEDIUM=14,
LARGE=16, EXTRA_LARGE=18) rather than indexing the family's sorted size
list by enum ordinal.
The ordinal-slot approach mis-selects whenever a family doesn't ship the
canonical {12,14,16,18} set: a family with only [10,14,18] would map
SMALL/MEDIUM/LARGE/EXTRA_LARGE to 10/14/18/18 — fine for SMALL but
arbitrary for the rest. Closest-pt always picks the on-disk file nearest
to the user-intended point size, with a deterministic smaller-pt
tie-break.
No change for canonical-sized families.
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
* **What changes are included?**
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< YES | PARTIALLY | NO
>**_
95 lines
3.2 KiB
C++
95 lines
3.2 KiB
C++
#include "SdCardFontManager.h"
|
||
|
||
#include <EpdFontFamily.h>
|
||
#include <GfxRenderer.h>
|
||
#include <Logging.h>
|
||
#include <SdCardFont.h>
|
||
#include <SdCardFontRegistry.h>
|
||
|
||
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<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) {
|
||
// 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) {
|
||
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;
|
||
}
|