diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 8bae555f..e339fe89 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -892,6 +892,58 @@ bool EpubReaderActivity::silentIndexNextChapterIfNeeded() { return true; } +void EpubReaderActivity::cacheMultiSpineChapter(const uint16_t vpWidth, const uint16_t vpHeight) { + if (!epub || !section) return; + + const int tocCount = epub->getTocItemsCount(); + if (tocCount == 0) return; + + const int currentTocIdx = section->getTocIndexForPage(section->currentPage); + if (currentTocIdx < 0) return; + + const int startSpine = epub->getSpineIndexForTocIndex(currentTocIdx); + if (startSpine < 0) return; + + // Chapter ends where the next TOC entry begins on a different spine + int endSpine = epub->getSpineItemsCount(); + for (int t = currentTocIdx + 1; t < tocCount; t++) { + const int tSpine = epub->getSpineIndexForTocIndex(t); + if (tSpine > startSpine) { + endSpine = tSpine; + break; + } + } + + // Collect uncached spines in this chapter + std::vector uncached; + uncached.reserve(endSpine - startSpine); + for (int i = startSpine; i < endSpine; i++) { + if (i == currentSpineIndex) continue; + auto cached = Section::readCachedPageCount(epub->getCachePath(), i, SETTINGS.getReaderFontId(), + SETTINGS.getReaderLineCompression(), SETTINGS.extraParagraphSpacing, + SETTINGS.paragraphAlignment, vpWidth, vpHeight, + SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, + SETTINGS.imageRendering); + if (!cached) uncached.push_back(i); + } + + if (uncached.empty()) return; + + const int total = static_cast(uncached.size()); + char buf[64]; + for (int idx = 0; idx < total; idx++) { + snprintf(buf, sizeof(buf), "%s (%d/%d)", tr(STR_INDEXING), idx + 1, total); + GUI.drawPopup(renderer, buf); + + Section s(epub, uncached[idx], renderer); + if (!s.createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(), + SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, vpWidth, vpHeight, + SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, SETTINGS.imageRendering)) { + LOG_ERR("ERS", "Multi-spine cache failed for spine %d", uncached[idx]); + } + } +} + // TODO: Failure handling void EpubReaderActivity::render(RenderLock&& lock) { if (!epub) { @@ -1011,6 +1063,10 @@ void EpubReaderActivity::render(RenderLock&& lock) { section->currentPage = newPage; pendingPercentJump = false; } + + const uint16_t vpW = renderer.getScreenWidth() - orientedMarginLeft - orientedMarginRight; + const uint16_t vpH = renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom; + cacheMultiSpineChapter(vpW, vpH); } renderer.clearScreen(); diff --git a/src/activities/reader/EpubReaderActivity.h b/src/activities/reader/EpubReaderActivity.h index 7a4a8db5..cbe6794c 100644 --- a/src/activities/reader/EpubReaderActivity.h +++ b/src/activities/reader/EpubReaderActivity.h @@ -40,6 +40,10 @@ class EpubReaderActivity final : public Activity { bool silentIndexingActive = false; bool silentIndexNextChapterIfNeeded(); + // Multi-spine chapter caching: proactively indexes all spine items that belong + // to the same TOC chapter so cross-spine page turns are instant. + void cacheMultiSpineChapter(uint16_t vpWidth, uint16_t vpHeight); + // Footnote support std::vector currentPageFootnotes; struct SavedPosition {