Merge pull request #241 from jpirnay/feat-large-image-placeholder
feat: Render larger images on demand not by default
This commit is contained in:
@@ -267,6 +267,9 @@ class CrossPointSettings {
|
||||
uint8_t showFileExtensions = 0;
|
||||
// Image rendering mode in EPUB reader
|
||||
uint8_t imageRendering = IMAGES_DISPLAY;
|
||||
// Show a placeholder for large images (>800×600 source pixels) instead of decoding immediately.
|
||||
// The user can press OK on the placeholder page to decode the image on demand.
|
||||
uint8_t largeImagePlaceholder = 1;
|
||||
// Dithering mode for decoded images (EPUB/JPG/PNG)
|
||||
uint8_t imageDithering = IMAGE_DITHER_BAYER;
|
||||
// Tilt-based page turning (X3 only — requires QMI8658 IMU)
|
||||
|
||||
@@ -139,6 +139,8 @@ inline const std::vector<SettingInfo> list = {
|
||||
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
|
||||
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
|
||||
"imageRendering", StrId::STR_CAT_READER),
|
||||
SettingInfo::Toggle(StrId::STR_LARGE_IMAGE_PLACEHOLDER, &CrossPointSettings::largeImagePlaceholder,
|
||||
"largeImagePlaceholder", StrId::STR_CAT_READER),
|
||||
SettingInfo::Value(StrId::STR_SCREEN_MARGIN, &CrossPointSettings::screenMargin, {5, 40, 5}, "screenMargin",
|
||||
StrId::STR_CAT_READER)
|
||||
.withSubmenu(StrId::STR_MENU_READER_SPACING),
|
||||
|
||||
@@ -407,6 +407,12 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
if (ev.type == ButtonEventManager::PressType::Short) {
|
||||
if (pageHasPlaceholders) {
|
||||
forceLoadLargeImages = true;
|
||||
pageHasPlaceholders = false;
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
openReaderMenu();
|
||||
return;
|
||||
}
|
||||
@@ -607,6 +613,8 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
: std::nullopt;
|
||||
if (resolvedPage) {
|
||||
section->currentPage = *resolvedPage;
|
||||
forceLoadLargeImages = false;
|
||||
pageHasPlaceholders = false;
|
||||
} else {
|
||||
navTarget =
|
||||
chapter.tocIndex ? NavigationTarget::makeTocIndex(*chapter.tocIndex) : NavigationTarget::makePage(0);
|
||||
@@ -1512,6 +1520,8 @@ bool EpubReaderActivity::stepPageState(const bool isForwardTurn) {
|
||||
}
|
||||
|
||||
lastPageTurnTime = millis();
|
||||
forceLoadLargeImages = false;
|
||||
pageHasPlaceholders = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1782,6 +1792,8 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
|
||||
navTarget.resolveInto(*section, currentSpineIndex);
|
||||
navTarget = NavigationTarget::makePage(section->currentPage);
|
||||
forceLoadLargeImages = false;
|
||||
pageHasPlaceholders = false;
|
||||
}
|
||||
|
||||
renderer.clearScreen();
|
||||
@@ -1949,15 +1961,20 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
}
|
||||
lastRenderStats.textAntiAliasing = aaEnabledForThisRender;
|
||||
|
||||
// Force special handling for pages with images when anti-aliasing is on
|
||||
bool imagePageWithAA = page->hasImages() && aaEnabledForThisRender;
|
||||
const bool effectiveForceLoad = forceLoadLargeImages || !SETTINGS.largeImagePlaceholder;
|
||||
pageHasPlaceholders = page->hasPlaceholderImages(effectiveForceLoad);
|
||||
|
||||
// Force special handling for pages with at least one real (decoded) image when anti-aliasing is on.
|
||||
// Mixed pages (some decoded, some placeholder) still need the AA codepath.
|
||||
bool imagePageWithAA =
|
||||
page->hasImages() && !page->allImagesArePlaceholders(effectiveForceLoad) && aaEnabledForThisRender;
|
||||
bool forceHalfRefreshThisPage = pendingHalfRefreshAfterImagePage && SETTINGS.halfRefreshAfterImagePage;
|
||||
pendingHalfRefreshAfterImagePage = false;
|
||||
lastRenderStats.imagePageWithAA = imagePageWithAA;
|
||||
lastRenderStats.forcedHalfRefresh = forceHalfRefreshThisPage;
|
||||
|
||||
logReaderMemSnapshot("before_bw_render");
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop, effectiveForceLoad);
|
||||
renderStatusBar();
|
||||
if (showTruncatedSectionHintThisRender) {
|
||||
const int hintX = orientedMarginLeft + 4;
|
||||
@@ -1993,7 +2010,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
|
||||
// Re-render page content to restore images into the blanked area
|
||||
// Status bar is not re-rendered here to avoid reading stale dynamic values (e.g. battery %)
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop);
|
||||
page->render(renderer, getEffectiveReaderFontId(), orientedMarginLeft, contentTop, effectiveForceLoad);
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
} else {
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
@@ -2051,7 +2068,10 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
LOG_INF("ERS", "Skipping grayscale/BW-restore for this page (insufficient heap for BW snapshot)");
|
||||
}
|
||||
|
||||
if (page->hasImages() && getEffectiveImageRendering() != CrossPointSettings::IMAGES_SUPPRESS) {
|
||||
// Only schedule the half-refresh if at least one real image was decoded on this page.
|
||||
// Placeholder-only pages don't deposit grayscale data that needs settling.
|
||||
if (page->hasImages() && !page->allImagesArePlaceholders(effectiveForceLoad) &&
|
||||
getEffectiveImageRendering() != CrossPointSettings::IMAGES_SUPPRESS) {
|
||||
pendingHalfRefreshAfterImagePage = true;
|
||||
}
|
||||
|
||||
@@ -2137,8 +2157,8 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
}
|
||||
|
||||
void EpubReaderActivity::renderPageContentOnly(const Page& page, const int orientedMarginTop,
|
||||
const int orientedMarginRight,
|
||||
const int orientedMarginBottom, const int orientedMarginLeft) {
|
||||
const int orientedMarginRight, const int orientedMarginBottom,
|
||||
const int orientedMarginLeft) {
|
||||
auto* fcm = renderer.getFontCacheManager();
|
||||
fcm->resetStats();
|
||||
|
||||
@@ -2155,8 +2175,8 @@ void EpubReaderActivity::renderPageContentOnly(const Page& page, const int orien
|
||||
}
|
||||
|
||||
void EpubReaderActivity::displayPreRenderedPage(const Page& page, const int orientedMarginTop,
|
||||
const int orientedMarginRight,
|
||||
const int orientedMarginBottom, const int orientedMarginLeft) {
|
||||
const int orientedMarginRight, const int orientedMarginBottom,
|
||||
const int orientedMarginLeft) {
|
||||
const int viewportHeight = std::max(0, renderer.getScreenHeight() - orientedMarginTop - orientedMarginBottom);
|
||||
const int contentTop = orientedMarginTop + getImageOnlyPageYOffset(page, viewportHeight);
|
||||
|
||||
@@ -2509,6 +2529,8 @@ void EpubReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION
|
||||
: std::nullopt;
|
||||
if (resolvedPage) {
|
||||
section->currentPage = *resolvedPage;
|
||||
forceLoadLargeImages = false;
|
||||
pageHasPlaceholders = false;
|
||||
} else {
|
||||
navTarget = chapter.tocIndex ? NavigationTarget::makeTocIndex(*chapter.tocIndex)
|
||||
: NavigationTarget::makePage(0);
|
||||
@@ -2535,6 +2557,8 @@ void EpubReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION
|
||||
if (newSpineIndex == currentSpineIndex) {
|
||||
if (const auto resolvedPage = section->getPageForTocIndex(nextTocIndex)) {
|
||||
section->currentPage = *resolvedPage;
|
||||
forceLoadLargeImages = false;
|
||||
pageHasPlaceholders = false;
|
||||
}
|
||||
} else {
|
||||
navTarget = NavigationTarget::makeTocIndex(nextTocIndex);
|
||||
|
||||
@@ -109,6 +109,11 @@ class EpubReaderActivity final : public Activity {
|
||||
unsigned long lastPageTurnTime = 0UL;
|
||||
unsigned long pageTurnDuration = 0UL;
|
||||
bool pendingHalfRefreshAfterImagePage = false;
|
||||
// When true, large images on the current page are decoded instead of shown as placeholders.
|
||||
// Reset to false on every page turn so the next image page starts with a placeholder again.
|
||||
bool forceLoadLargeImages = false;
|
||||
// Set after each render: true if the current page contains at least one placeholder image.
|
||||
bool pageHasPlaceholders = false;
|
||||
// Temporary AA suspension when BW snapshot allocation fails under memory pressure.
|
||||
// Automatically lifted once heap recovers above hysteresis thresholds.
|
||||
bool antiAliasingSuspendedLowMemory = false;
|
||||
@@ -254,8 +259,8 @@ class EpubReaderActivity final : public Activity {
|
||||
// Draws the status bar over the current frame buffer and flushes to the display.
|
||||
// Handles the refresh cycle and grayscale AA pass. page must be the same page
|
||||
// that was last rendered into the buffer (needed for image AA re-render).
|
||||
void displayPreRenderedPage(const Page& page, int orientedMarginTop, int orientedMarginRight, int orientedMarginBottom,
|
||||
int orientedMarginLeft);
|
||||
void displayPreRenderedPage(const Page& page, int orientedMarginTop, int orientedMarginRight,
|
||||
int orientedMarginBottom, int orientedMarginLeft);
|
||||
void renderStatusBar() const;
|
||||
void silentIndexNextChapterIfNeeded(uint16_t viewportWidth, uint16_t viewportHeight);
|
||||
void saveProgress(int spineIndex, int currentPage, int pageCount);
|
||||
|
||||
Reference in New Issue
Block a user