diff --git a/lib/EpdFont/FontDecompressor.cpp b/lib/EpdFont/FontDecompressor.cpp index 38fe095c..8cdc1d12 100644 --- a/lib/EpdFont/FontDecompressor.cpp +++ b/lib/EpdFont/FontDecompressor.cpp @@ -5,6 +5,7 @@ #include #include +#include FontDecompressor::~FontDecompressor() { deinit(); } @@ -263,12 +264,14 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 // Step 2: Compute total buffer size and collect unique groups uint32_t totalBytes = 0; uint16_t neededGroups[128]; + uint16_t neededGlyphGroups[MAX_PAGE_GLYPHS]; // parallel to neededGlyphs; avoids re-calling getGroupIndex later uint8_t groupCount = 0; bool groupCapWarned = false; for (uint16_t i = 0; i < glyphCount; i++) { totalBytes += fontData->glyph[neededGlyphs[i]].dataLength; - uint16_t gi = getGroupIndex(fontData, neededGlyphs[i]); + const uint16_t gi = getGroupIndex(fontData, neededGlyphs[i]); + neededGlyphGroups[i] = gi; bool found = false; for (uint8_t j = 0; j < groupCount; j++) { if (neededGroups[j] == gi) { @@ -319,7 +322,7 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 // Initialize lookup entries (bufferOffset = UINT32_MAX means not yet extracted) for (uint16_t i = 0; i < glyphCount; i++) { - slot.glyphs[i] = {neededGlyphs[i], UINT32_MAX, 0}; + slot.glyphs[i] = {neededGlyphs[i], UINT32_MAX, 0, neededGlyphGroups[i]}; } // Sort by glyphIndex for binary search in getBitmap() @@ -338,21 +341,25 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 uint32_t groupAlignedTracker[128] = {}; // running byte-aligned offset for each needed group if (fontData->glyphToGroup) { - // Frequency-grouped: single O(totalGlyphs) pass through glyphToGroup + // Frequency-grouped: single O(totalGlyphs) pass through glyphToGroup. + // Reverse map (fontGroupIdx → position in neededGroups) replaces the inner + // linear scan, dropping this pass from O(totalGlyphs × groupCount) to O(totalGlyphs). + uint8_t* groupIdToPos = static_cast(malloc(fontData->groupCount)); + if (!groupIdToPos) { + LOG_ERR("FDC", "OOM: cannot allocate %u bytes for groupIdToPos map", fontData->groupCount); + freePageBuffer(); + return glyphCount; + } + memset(groupIdToPos, 0xFF, fontData->groupCount); + for (uint8_t j = 0; j < groupCount; j++) groupIdToPos[neededGroups[j]] = j; + const auto& lastInterval = fontData->intervals[fontData->intervalCount - 1]; const uint32_t totalGlyphs = lastInterval.offset + (lastInterval.last - lastInterval.first + 1); for (uint32_t i = 0; i < totalGlyphs; i++) { const uint16_t gi = fontData->glyphToGroup[i]; - // Find this glyph's group position in neededGroups - uint8_t gpPos = groupCount; - for (uint8_t j = 0; j < groupCount; j++) { - if (neededGroups[j] == gi) { - gpPos = j; - break; - } - } - if (gpPos == groupCount) continue; // not a needed group + const uint8_t gpPos = groupIdToPos[gi]; + if (gpPos == 0xFF) continue; // not a needed group const EpdGlyph& glyph = fontData->glyph[i]; @@ -374,6 +381,8 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 groupAlignedTracker[gpPos] += ((glyph.width + 3) / 4) * glyph.height; } } + + free(groupIdToPos); } else { // Contiguous-group: iterate each needed group's glyphs directly for (uint8_t g = 0; g < groupCount; g++) { @@ -432,11 +441,10 @@ int FontDecompressor::prewarmCache(const EpdFontData* fontData, const char* utf8 // alignedOffset was pre-computed in step 3b — no full-group compact scan needed. for (uint16_t i = 0; i < slot.glyphCount; i++) { if (slot.glyphs[i].bufferOffset != UINT32_MAX) continue; // already extracted - if (getGroupIndex(fontData, slot.glyphs[i].glyphIndex) != groupIdx) continue; + if (slot.glyphs[i].groupIndex != groupIdx) continue; const EpdGlyph& glyph = fontData->glyph[slot.glyphs[i].glyphIndex]; - compactSingleGlyph(&groupBuf[slot.glyphs[i].alignedOffset], &slot.buffer[writeOffset], glyph.width, - glyph.height); + compactSingleGlyph(&groupBuf[slot.glyphs[i].alignedOffset], &slot.buffer[writeOffset], glyph.width, glyph.height); slot.glyphs[i].bufferOffset = writeOffset; writeOffset += glyph.dataLength; } diff --git a/lib/EpdFont/FontDecompressor.h b/lib/EpdFont/FontDecompressor.h index 2894406a..cdf24b9a 100644 --- a/lib/EpdFont/FontDecompressor.h +++ b/lib/EpdFont/FontDecompressor.h @@ -52,6 +52,7 @@ class FontDecompressor { uint32_t glyphIndex; uint32_t bufferOffset; uint32_t alignedOffset; // byte-aligned offset within its decompressed group (set during prewarm pre-scan) + uint16_t groupIndex; // cached to avoid re-calling getGroupIndex in prewarm Step 4 }; struct PageSlot { uint8_t* buffer = nullptr; diff --git a/lib/Epub/Epub/ParsedText.cpp b/lib/Epub/Epub/ParsedText.cpp index aad424cf..ecac74c1 100644 --- a/lib/Epub/Epub/ParsedText.cpp +++ b/lib/Epub/Epub/ParsedText.cpp @@ -35,6 +35,8 @@ bool isClosingPunctuation(const uint32_t cp) { case 0x2019: // ' right single quotation mark case 0x201D: // " right double quotation mark case 0x2026: // … ellipsis + case 0x2013: // – en dash + case 0x2014: // — em dash return true; default: return false;