Backup: Stable state with EPUB reader fixes (Freeze, OOM, Speed, State, Tooling)

This commit is contained in:
Antigravity Agent
2026-01-19 18:44:45 -05:00
parent 21277e03eb
commit 1237f01ac2
34 changed files with 1600 additions and 192 deletions

View File

@@ -2,7 +2,13 @@
#include <Utf8.h>
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); }
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap[fontId] = font; }
void GfxRenderer::clearCustomFonts(const int startId) {
for (auto it = fontMap.lower_bound(startId); it != fontMap.end();) {
it = fontMap.erase(it);
}
}
void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const {
switch (orientation) {
@@ -101,10 +107,12 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
// no printable characters
if (!font.hasPrintableChars(text, style)) {
Serial.printf("[%lu] [GFX] text '%s' has no printable chars\n", millis(), text);
return;
}
uint32_t cp;
const char* p = text;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
renderChar(font, cp, &xpos, &yPos, black, style);
}
@@ -164,8 +172,6 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
bool isScaled = false;
int cropPixX = std::floor(bitmap.getWidth() * cropX / 2.0f);
int cropPixY = std::floor(bitmap.getHeight() * cropY / 2.0f);
Serial.printf("[%lu] [GFX] Cropping %dx%d by %dx%d pix, is %s\n", millis(), bitmap.getWidth(), bitmap.getHeight(),
cropPixX, cropPixY, bitmap.isTopDown() ? "top-down" : "bottom-up");
if (maxWidth > 0 && (1.0f - cropX) * bitmap.getWidth() > maxWidth) {
scale = static_cast<float>(maxWidth) / static_cast<float>((1.0f - cropX) * bitmap.getWidth());
@@ -175,7 +181,6 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
scale = std::min(scale, static_cast<float>(maxHeight) / static_cast<float>((1.0f - cropY) * bitmap.getHeight()));
isScaled = true;
}
Serial.printf("[%lu] [GFX] Scaling by %f - %s\n", millis(), scale, isScaled ? "scaled" : "not scaled");
// Calculate output row size (2 bits per pixel, packed into bytes)
// IMPORTANT: Use int, not uint8_t, to avoid overflow for images > 1020 pixels wide
@@ -446,7 +451,16 @@ int GfxRenderer::getSpaceWidth(const int fontId) const {
return 0;
}
return fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
const EpdGlyph* glyph = fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR);
if (!glyph) {
// Serial.printf("[%lu] [GFX] Font %d (Regular) has no space glyph! Using fallback.\n", millis(), fontId);
const EpdFontData* data = fontMap.at(fontId).getData(EpdFontFamily::REGULAR);
if (data) {
return data->ascender / 3;
}
return 0;
}
return glyph->advanceX;
}
int GfxRenderer::getFontAscenderSize(const int fontId) const {
@@ -455,7 +469,13 @@ int GfxRenderer::getFontAscenderSize(const int fontId) const {
return 0;
}
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
const EpdFontData* data = fontMap.at(fontId).getData(EpdFontFamily::REGULAR);
if (!data) {
Serial.printf("[%lu] [GFX] Font %d (Regular) has no data!\n", millis(), fontId);
return 0;
}
return data->ascender;
}
int GfxRenderer::getLineHeight(const int fontId) const {
@@ -464,7 +484,13 @@ int GfxRenderer::getLineHeight(const int fontId) const {
return 0;
}
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->advanceY;
const EpdFontData* data = fontMap.at(fontId).getData(EpdFontFamily::REGULAR);
if (!data) {
Serial.printf("[%lu] [GFX] Font %d (Regular) has no data!\n", millis(), fontId);
return 0;
}
return data->advanceY;
}
void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3,
@@ -595,7 +621,9 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
const int left = glyph->left;
const int top = glyph->top;
const uint8_t* bitmap = &font.getData(style)->bitmap[offset];
// Use loadGlyphBitmap to support both static and custom (SD-based) fonts
uint8_t* buffer = nullptr; // Not used for now, as we expect a pointer or cache
const uint8_t* bitmap = font.loadGlyphBitmap(glyph, buffer, style);
if (bitmap != nullptr) {
for (int glyphY = 0; glyphY < height; glyphY++) {
@@ -695,8 +723,6 @@ bool GfxRenderer::storeBwBuffer() {
memcpy(bwBufferChunks[i], frameBuffer + offset, BW_BUFFER_CHUNK_SIZE);
}
Serial.printf("[%lu] [GFX] Stored BW buffer in %zu chunks (%zu bytes each)\n", millis(), BW_BUFFER_NUM_CHUNKS,
BW_BUFFER_CHUNK_SIZE);
return true;
}
@@ -742,7 +768,6 @@ void GfxRenderer::restoreBwBuffer() {
einkDisplay.cleanupGrayscaleBuffers(frameBuffer);
freeBwBufferChunks();
Serial.printf("[%lu] [GFX] Restored and freed BW buffer chunks\n", millis());
}
/**
@@ -760,24 +785,25 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp,
const bool pixelState, const EpdFontFamily::Style style) const {
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);
if (!glyph) {
// TODO: Replace with fallback glyph property?
glyph = fontFamily.getGlyph('?', style);
}
// no glyph?
if (!glyph) {
Serial.printf("[%lu] [GFX] No glyph for codepoint %d\n", millis(), cp);
return;
}
const int is2Bit = fontFamily.getData(style)->is2Bit;
const uint32_t offset = glyph->dataOffset;
const EpdFont* font = fontFamily.getFont(style);
if (!font) return;
const EpdFontData* data = font->getData(style);
if (!data) return;
const int is2Bit = data->is2Bit;
const uint8_t width = glyph->width;
const uint8_t height = glyph->height;
const int left = glyph->left;
const uint8_t* bitmap = nullptr;
bitmap = &fontFamily.getData(style)->bitmap[offset];
const uint8_t* bitmap = font->loadGlyphBitmap(glyph, nullptr, style);
if (bitmap != nullptr) {
for (int glyphY = 0; glyphY < height; glyphY++) {