From db94a86fba78d9d9823c2dde9d8e103af7031cf6 Mon Sep 17 00:00:00 2001 From: Uri Tauber Date: Tue, 2 Jun 2026 16:47:24 +0300 Subject: [PATCH] fix: Skip Underline Calculations During Font Cache Scan Pass (#2237) ## Summary This PR optimizes the text rendering process by skipping underline style calculations and measurements during the initial font cache scanning phase. This prevents excessive and unnecessary SD card reads on pages with heavy use of underlines (e.g., Table of Contents pages). ### **The Problem** During the first rendering pass (the font cache scan pass used to collect text for prewarming), `GfxRenderer::drawText()` early-returns after recording text as expected. However, `TextBlock::render()` continued past this point to execute the underline decoration logic. Because underline calculation calls `getTextWidth()` and `getTextAdvanceX()`, it triggered immediate glyph lookups via `EpdFont::getGlyph()`. Since the SD card font had not been prewarmed yet at this stage, the lookups fell back to the `glyphMissHandler`, resulting in hundreds of individual, slow SD card reads into a limited 8-slot ring buffer. ### **The Fix** 1. **Exposed Scan State:** Added `GfxRenderer::isFontCacheScanning()` to safely check if the font cache manager is currently in text-collection/scan mode. 2. **Bypassed Underline Logic:** Modified `TextBlock::render()` to check this state and skip underline measurement and drawing entirely while scanning is active. > [!NOTE] > The text itself is still properly captured for prewarming via `drawText()`. The underlines will be safely and efficiently calculated and drawn during the actual render pass after the fonts have been completely prewarmed. --- ### 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 >**_ --- lib/Epub/Epub/blocks/TextBlock.cpp | 3 ++- lib/GfxRenderer/GfxRenderer.cpp | 2 ++ lib/GfxRenderer/GfxRenderer.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Epub/Epub/blocks/TextBlock.cpp b/lib/Epub/Epub/blocks/TextBlock.cpp index a9fefb25..3549087e 100644 --- a/lib/Epub/Epub/blocks/TextBlock.cpp +++ b/lib/Epub/Epub/blocks/TextBlock.cpp @@ -19,6 +19,7 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int return; } + const bool scanning = renderer.isFontCacheScanning(); const int ascender = renderer.getFontAscenderSize(fontId); for (size_t i = 0; i < words.size(); i++) { const int wordX = wordXpos[i] + x; @@ -58,7 +59,7 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int renderer.drawText(fontId, wordX, wordY, words[i].c_str(), true, currentStyle, baseDir); } - if ((currentStyle & EpdFontFamily::UNDERLINE) != 0) { + if (!scanning && (currentStyle & EpdFontFamily::UNDERLINE) != 0) { const std::string& w = words[i]; const int fullWordWidth = renderer.getTextWidth(fontId, w.c_str(), currentStyle, baseDir); // y is the top of the text line; add ascender to reach baseline, then offset 2px below diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index e5246a07..759b2ad4 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -91,6 +91,8 @@ void GfxRenderer::begin() { bwBufferChunks.assign((frameBufferSize + BW_BUFFER_CHUNK_SIZE - 1) / BW_BUFFER_CHUNK_SIZE, nullptr); } +bool GfxRenderer::isFontCacheScanning() const { return fontCacheManager_ && fontCacheManager_->isScanning(); } + void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { auto result = fontMap.insert({fontId, font}); if (!result.second) { diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 16aa6de9..924e5c0b 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -104,6 +104,7 @@ class GfxRenderer { } void setFontCacheManager(FontCacheManager* m) { fontCacheManager_ = m; } FontCacheManager* getFontCacheManager() const { return fontCacheManager_; } + bool isFontCacheScanning() const; const std::map& getFontMap() const { return fontMap; } void registerSdCardFont(int fontId, SdCardFont* font) { sdCardFonts_[fontId] = font; } void unregisterSdCardFont(int fontId) { removeFont(fontId); }