Xteink-X4-crosspoint-reader/lib/EpdFont/SdFontFamily.cpp
Eunchurn Park 68ce6db291
feat: Add custom font selection from SD card
Allow users to select custom fonts (.epdfont files) from the
/.crosspoint/fonts/ directory on the SD card for EPUB/TXT reading.

Features:
- New FontSelectionActivity for browsing and selecting fonts
- SdFont and SdFontFamily classes for loading fonts from SD card
- Dynamic font reloading without device reboot
- Reader cache invalidation when font changes
- Hash-based font ID generation for proper cache management

The custom fonts use the .epdfont binary format which supports:
- 2-bit antialiasing for smooth text rendering
- Efficient on-demand glyph loading with LRU cache
- Memory-optimized design for ESP32-C3 constraints
2026-01-18 18:46:23 +09:00

297 lines
8.1 KiB
C++

#include "SdFontFamily.h"
#include <HardwareSerial.h>
// ============================================================================
// SdFontFamily Implementation
// ============================================================================
SdFontFamily::SdFontFamily(const char* regularPath, const char* boldPath, const char* italicPath,
const char* boldItalicPath)
: regular(nullptr), bold(nullptr), italic(nullptr), boldItalic(nullptr), ownsPointers(true) {
if (regularPath) {
regular = new SdFont(regularPath);
}
if (boldPath) {
bold = new SdFont(boldPath);
}
if (italicPath) {
italic = new SdFont(italicPath);
}
if (boldItalicPath) {
boldItalic = new SdFont(boldItalicPath);
}
}
SdFontFamily::~SdFontFamily() {
if (ownsPointers) {
delete regular;
delete bold;
delete italic;
delete boldItalic;
}
}
SdFontFamily::SdFontFamily(SdFontFamily&& other) noexcept
: regular(other.regular),
bold(other.bold),
italic(other.italic),
boldItalic(other.boldItalic),
ownsPointers(other.ownsPointers) {
other.regular = nullptr;
other.bold = nullptr;
other.italic = nullptr;
other.boldItalic = nullptr;
other.ownsPointers = false;
}
SdFontFamily& SdFontFamily::operator=(SdFontFamily&& other) noexcept {
if (this != &other) {
if (ownsPointers) {
delete regular;
delete bold;
delete italic;
delete boldItalic;
}
regular = other.regular;
bold = other.bold;
italic = other.italic;
boldItalic = other.boldItalic;
ownsPointers = other.ownsPointers;
other.regular = nullptr;
other.bold = nullptr;
other.italic = nullptr;
other.boldItalic = nullptr;
other.ownsPointers = false;
}
return *this;
}
bool SdFontFamily::load() {
bool success = true;
if (regular && !regular->load()) {
Serial.printf("[%lu] [SdFontFamily] Failed to load regular font\n", millis());
success = false;
}
if (bold && !bold->load()) {
Serial.printf("[%lu] [SdFontFamily] Failed to load bold font\n", millis());
// Bold is optional, don't fail completely
}
if (italic && !italic->load()) {
Serial.printf("[%lu] [SdFontFamily] Failed to load italic font\n", millis());
// Italic is optional
}
if (boldItalic && !boldItalic->load()) {
Serial.printf("[%lu] [SdFontFamily] Failed to load bold-italic font\n", millis());
// Bold-italic is optional
}
return success;
}
bool SdFontFamily::isLoaded() const { return regular && regular->isLoaded(); }
SdFont* SdFontFamily::getFont(EpdFontStyle style) const {
if (style == BOLD && bold && bold->isLoaded()) {
return bold;
}
if (style == ITALIC && italic && italic->isLoaded()) {
return italic;
}
if (style == BOLD_ITALIC) {
if (boldItalic && boldItalic->isLoaded()) {
return boldItalic;
}
if (bold && bold->isLoaded()) {
return bold;
}
if (italic && italic->isLoaded()) {
return italic;
}
}
return regular;
}
void SdFontFamily::getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style) const {
SdFont* font = getFont(style);
if (font) {
font->getTextDimensions(string, w, h);
} else {
*w = 0;
*h = 0;
}
}
bool SdFontFamily::hasPrintableChars(const char* string, EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->hasPrintableChars(string) : false;
}
const EpdGlyph* SdFontFamily::getGlyph(uint32_t cp, EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->getGlyph(cp) : nullptr;
}
const uint8_t* SdFontFamily::getGlyphBitmap(uint32_t cp, EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->getGlyphBitmap(cp) : nullptr;
}
uint8_t SdFontFamily::getAdvanceY(EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->getAdvanceY() : 0;
}
int8_t SdFontFamily::getAscender(EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->getAscender() : 0;
}
int8_t SdFontFamily::getDescender(EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->getDescender() : 0;
}
bool SdFontFamily::is2Bit(EpdFontStyle style) const {
SdFont* font = getFont(style);
return font ? font->is2Bit() : false;
}
// ============================================================================
// UnifiedFontFamily Implementation
// ============================================================================
UnifiedFontFamily::UnifiedFontFamily(const EpdFontFamily* font) : type(Type::FLASH), flashFont(font), sdFont(nullptr) {}
UnifiedFontFamily::UnifiedFontFamily(SdFontFamily* font) : type(Type::SD), flashFont(nullptr), sdFont(font) {}
UnifiedFontFamily::~UnifiedFontFamily() {
// flashFont is not owned (points to global), don't delete
delete sdFont;
}
UnifiedFontFamily::UnifiedFontFamily(UnifiedFontFamily&& other) noexcept
: type(other.type), flashFont(other.flashFont), sdFont(other.sdFont) {
other.flashFont = nullptr;
other.sdFont = nullptr;
}
UnifiedFontFamily& UnifiedFontFamily::operator=(UnifiedFontFamily&& other) noexcept {
if (this != &other) {
// flashFont is not owned (points to global), don't delete
delete sdFont;
type = other.type;
flashFont = other.flashFont;
sdFont = other.sdFont;
other.flashFont = nullptr;
other.sdFont = nullptr;
}
return *this;
}
void UnifiedFontFamily::getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
flashFont->getTextDimensions(string, w, h, style);
} else if (sdFont) {
sdFont->getTextDimensions(string, w, h, style);
} else {
*w = 0;
*h = 0;
}
}
bool UnifiedFontFamily::hasPrintableChars(const char* string, EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
return flashFont->hasPrintableChars(string, style);
} else if (sdFont) {
return sdFont->hasPrintableChars(string, style);
}
return false;
}
const EpdGlyph* UnifiedFontFamily::getGlyph(uint32_t cp, EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
return flashFont->getGlyph(cp, style);
} else if (sdFont) {
return sdFont->getGlyph(cp, style);
}
return nullptr;
}
const uint8_t* UnifiedFontFamily::getGlyphBitmap(uint32_t cp, EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
// For flash fonts, get bitmap from the data structure
const EpdFontData* data = flashFont->getData(style);
const EpdGlyph* glyph = flashFont->getGlyph(cp, style);
if (data && glyph) {
return &data->bitmap[glyph->dataOffset];
}
return nullptr;
} else if (sdFont) {
return sdFont->getGlyphBitmap(cp, style);
}
return nullptr;
}
uint8_t UnifiedFontFamily::getAdvanceY(EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
const EpdFontData* data = flashFont->getData(style);
return data ? data->advanceY : 0;
} else if (sdFont) {
return sdFont->getAdvanceY(style);
}
return 0;
}
int8_t UnifiedFontFamily::getAscender(EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
const EpdFontData* data = flashFont->getData(style);
return data ? data->ascender : 0;
} else if (sdFont) {
return sdFont->getAscender(style);
}
return 0;
}
int8_t UnifiedFontFamily::getDescender(EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
const EpdFontData* data = flashFont->getData(style);
return data ? data->descender : 0;
} else if (sdFont) {
return sdFont->getDescender(style);
}
return 0;
}
bool UnifiedFontFamily::is2Bit(EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
const EpdFontData* data = flashFont->getData(style);
return data ? data->is2Bit : false;
} else if (sdFont) {
return sdFont->is2Bit(style);
}
return false;
}
const EpdFontData* UnifiedFontFamily::getFlashData(EpdFontStyle style) const {
if (type == Type::FLASH && flashFont) {
return flashFont->getData(style);
}
return nullptr;
}
bool UnifiedFontFamily::hasBold() const {
if (type == Type::FLASH && flashFont) {
return flashFont->hasBold();
} else if (sdFont) {
return sdFont->hasBold();
}
return false;
}