From 905f69457631ce7a203aca71db28cb6eaf38f826 Mon Sep 17 00:00:00 2001 From: cottongin Date: Thu, 12 Feb 2026 16:13:55 -0500 Subject: [PATCH] prerender book covers and thumbnails when opening a book for the first time Moves cover/thumbnail generation from lazy (Home screen, Sleep screen) into each reader activity's onEnter(). On first open, generates all needed BMPs (cover, cover_crop, thumbnails for all theme heights) with a "Preparing book..." progress popup. Subsequent opens skip instantly when files exist. Co-authored-by: Cursor --- src/activities/reader/EpubReaderActivity.cpp | 36 ++++++++++++++++++++ src/activities/reader/TxtReaderActivity.cpp | 9 +++++ src/activities/reader/XtcReaderActivity.cpp | 31 +++++++++++++++++ src/components/UITheme.h | 5 +++ 4 files changed, 81 insertions(+) diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 8b2e47e2..9c292f41 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -102,6 +102,42 @@ void EpubReaderActivity::onEnter() { } } + // Prerender covers and thumbnails on first open so Home and Sleep screens are instant. + // Each generate* call is a no-op if the file already exists, so this only does work once. + { + int totalSteps = 0; + if (!Storage.exists(epub->getCoverBmpPath(false).c_str())) totalSteps++; + if (!Storage.exists(epub->getCoverBmpPath(true).c_str())) totalSteps++; + for (int i = 0; i < PRERENDER_THUMB_HEIGHTS_COUNT; i++) { + if (!Storage.exists(epub->getThumbBmpPath(PRERENDER_THUMB_HEIGHTS[i]).c_str())) totalSteps++; + } + + if (totalSteps > 0) { + Rect popupRect = GUI.drawPopup(renderer, "Preparing book..."); + int completedSteps = 0; + + auto updateProgress = [&]() { + completedSteps++; + GUI.fillPopupProgress(renderer, popupRect, completedSteps * 100 / totalSteps); + }; + + if (!Storage.exists(epub->getCoverBmpPath(false).c_str())) { + epub->generateCoverBmp(false); + updateProgress(); + } + if (!Storage.exists(epub->getCoverBmpPath(true).c_str())) { + epub->generateCoverBmp(true); + updateProgress(); + } + for (int i = 0; i < PRERENDER_THUMB_HEIGHTS_COUNT; i++) { + if (!Storage.exists(epub->getThumbBmpPath(PRERENDER_THUMB_HEIGHTS[i]).c_str())) { + epub->generateThumbBmp(PRERENDER_THUMB_HEIGHTS[i]); + updateProgress(); + } + } + } + } + // Save current epub as last opened epub and add to recent books APP_STATE.openEpubPath = epub->getPath(); APP_STATE.saveToFile(); diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 441d5700..5c2b38ee 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -57,6 +57,15 @@ void TxtReaderActivity::onEnter() { txt->setupCacheDir(); + // Prerender cover on first open so the Sleep screen is instant. + // generateCoverBmp() is a no-op if the file already exists, so this only does work once. + // TXT has no thumbnail support, so only the sleep screen cover is generated. + if (!Storage.exists(txt->getCoverBmpPath().c_str())) { + Rect popupRect = GUI.drawPopup(renderer, "Preparing book..."); + txt->generateCoverBmp(); + GUI.fillPopupProgress(renderer, popupRect, 100); + } + // Save current txt as last opened file and add to recent books auto filePath = txt->getPath(); auto fileName = filePath.substr(filePath.rfind('/') + 1); diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 31795fa8..7297d5eb 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -43,6 +43,37 @@ void XtcReaderActivity::onEnter() { // Load saved progress loadProgress(); + // Prerender covers and thumbnails on first open so Home and Sleep screens are instant. + // Each generate* call is a no-op if the file already exists, so this only does work once. + { + int totalSteps = 0; + if (!Storage.exists(xtc->getCoverBmpPath().c_str())) totalSteps++; + for (int i = 0; i < PRERENDER_THUMB_HEIGHTS_COUNT; i++) { + if (!Storage.exists(xtc->getThumbBmpPath(PRERENDER_THUMB_HEIGHTS[i]).c_str())) totalSteps++; + } + + if (totalSteps > 0) { + Rect popupRect = GUI.drawPopup(renderer, "Preparing book..."); + int completedSteps = 0; + + auto updateProgress = [&]() { + completedSteps++; + GUI.fillPopupProgress(renderer, popupRect, completedSteps * 100 / totalSteps); + }; + + if (!Storage.exists(xtc->getCoverBmpPath().c_str())) { + xtc->generateCoverBmp(); + updateProgress(); + } + for (int i = 0; i < PRERENDER_THUMB_HEIGHTS_COUNT; i++) { + if (!Storage.exists(xtc->getThumbBmpPath(PRERENDER_THUMB_HEIGHTS[i]).c_str())) { + xtc->generateThumbBmp(PRERENDER_THUMB_HEIGHTS[i]); + updateProgress(); + } + } + } + } + // Save current XTC as last opened book and add to recent books APP_STATE.openEpubPath = xtc->getPath(); APP_STATE.saveToFile(); diff --git a/src/components/UITheme.h b/src/components/UITheme.h index 0a30223b..bf8595c0 100644 --- a/src/components/UITheme.h +++ b/src/components/UITheme.h @@ -27,5 +27,10 @@ class UITheme { const BaseTheme* currentTheme; }; +// Known theme thumbnail heights to prerender when opening a book for the first time. +// These correspond to homeCoverHeight values across all themes (Lyra=226, Base=400). +static constexpr int PRERENDER_THUMB_HEIGHTS[] = {226, 400}; +static constexpr int PRERENDER_THUMB_HEIGHTS_COUNT = 2; + // Helper macro to access current theme #define GUI UITheme::getInstance().getTheme()