Skip to content

fix: use double FAST_REFRESH to prevent washout on large grey images#957

Merged
daveallie merged 1 commit intocrosspoint-reader:masterfrom
martinbrook:mbrook-artimus
Feb 20, 2026
Merged

fix: use double FAST_REFRESH to prevent washout on large grey images#957
daveallie merged 1 commit intocrosspoint-reader:masterfrom
martinbrook:mbrook-artimus

Conversation

@martinbrook
Copy link
Contributor

@martinbrook martinbrook commented Feb 17, 2026

Summary

Fixes #1011

Use double FAST_REFRESH for image pages to prevent grayscale washout, HALF_REFRESH sets e-ink particles too firmly for the grayscale LUT to adjust, causing washed-out images (especially large, light-gray ones). Replace HALF_REFRESH with @pablohc's double FAST_REFRESH technique: blank only the image bounding box area, then re-render with images. This clears ghosting while keeping particles loosely set for grayscale.

Additional Context


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? < PARTIALLY >

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

Adds Page APIs to access an image's ImageBlock and compute the union bounding box of all images on a page; updates EpubReaderActivity rendering to use an image-with-AA path that blanks the image box and performs two FAST_REFRESH passes with a HALF_REFRESH fallback.

Changes

Cohort / File(s) Summary
Image Access API
lib/Epub/Epub/Page.h
Added const ImageBlock& getImageBlock() const to PageImage and bool getImageBoundingBox(int16_t& outX, int16_t& outY, int16_t& outW, int16_t& outH) const to Page to compute the union bounding box of all images in page-local coordinates.
Refresh Logic
src/activities/reader/EpubReaderActivity.cpp
Changed refresh control flow for image pages with anti-aliasing: when bounding box is available, blank that area, perform FAST_REFRESH, render images and status bar, then FAST_REFRESH again; fallback to HALF_REFRESH if no bounding box. Other refresh cadence unchanged; double FAST_REFRESH does not count toward full-refresh cadence.

Sequence Diagram

sequenceDiagram
    participant RA as EpubReaderActivity
    participant Page as Page
    participant Render as RenderingSystem

    alt Image page with AA and bounding box available
        RA->>Page: getImageBoundingBox()
        Page-->>RA: bounding box (x,y,w,h)
        RA->>Render: clearArea(x,y,w,h)
        RA->>Render: FAST_REFRESH
        RA->>Page: render page (with images)
        RA->>Render: render status bar
        RA->>Render: FAST_REFRESH
    else Image page with AA but no bounding box
        RA->>Render: HALF_REFRESH (fallback)
    else Non-image or non-AA page
        RA->>Render: standard HALF_REFRESH or full render as per cadence
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: using double FAST_REFRESH to prevent washout on large grey images, which matches the primary change in the code.
Linked Issues check ✅ Passed The changes address issue #1011 by implementing the double FAST_REFRESH technique to fix grayscale image washout. New API methods enable bounding box computation needed for the refresh strategy.
Out of Scope Changes check ✅ Passed All changes are directly in scope: new Page API for image bounding box, new PageImage accessor, and refresh logic updates in EpubReaderActivity to implement the double FAST_REFRESH technique.
Description check ✅ Passed The pull request description clearly references issue #1011 and explains the technical change: replacing HALF_REFRESH with double FAST_REFRESH to fix washed-out grayscale images.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@martinbrook martinbrook marked this pull request as draft February 17, 2026 21:22
@martinbrook martinbrook changed the title fix: use double FAST_REFRESH to prevent washout on large grey image fix: use double FAST_REFRESH to prevent washout on large grey images Feb 17, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 530d439 and 75d5266.

📒 Files selected for processing (2)
  • lib/Epub/Epub/Page.h
  • src/activities/reader/EpubReaderActivity.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().

Applied to files:

  • src/activities/reader/EpubReaderActivity.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Title Check
  • GitHub Check: build
  • GitHub Check: cppcheck
🔇 Additional comments (2)
lib/Epub/Epub/Page.h (2)

52-52: LGTM - accessor provides necessary access for the new rendering flow.

The const reference return is appropriate here. Note: This assumes imageBlock is never null, which appears to be a class invariant given the constructor takes an ImageBlock shared_ptr.


68-95: Clean bounding box implementation with one minor edge-case note.

The algorithm correctly computes the union of all image rectangles. The use of INT16_MAX/INT16_MIN for initialization and early-return on no images is appropriate.

Minor note: Lines 79-80 compute right = x + width and bottom = y + height. If an image is positioned near coordinate limits, this could overflow int16_t. For typical e-ink display dimensions this is unlikely, but if dimensions can exceed ~32K, consider using int32_t for intermediate calculations.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/activities/reader/EpubReaderActivity.cpp`:
- Around line 640-663: pagesUntilFullRefresh is always decremented inside the
imagePageWithAA branch which lets it go negative because the normal reset check
is only outside that branch; fix by adding the same reset logic inside the
imagePageWithAA handling: after pagesUntilFullRefresh--, check if
(pagesUntilFullRefresh <= 1) pagesUntilFullRefresh = refreshFrequency (or the
appropriate full-refresh interval variable used elsewhere) so image pages with
AA do not drive the counter negative; alternatively, if you prefer not to count
those pages at all, simply omit the pagesUntilFullRefresh-- inside the
imagePageWithAA branch (update the code around page->getImageBoundingBox /
renderer.displayBuffer logic accordingly).

…hout

HALF_REFRESH sets e-ink particles too firmly for the grayscale LUT to
adjust, causing washed-out images (especially large, light-gray ones).
Replace HALF_REFRESH with pablohc's double FAST_REFRESH technique:
blank only the image bounding box area, then re-render with images.
This clears ghosting while keeping particles loosely set for grayscale.
@martinbrook
Copy link
Contributor Author

issue originally mentioned at 6c3a615#commitcomment-177296177

@martinbrook
Copy link
Contributor Author

1000016127
1000016126

@martinbrook
Copy link
Contributor Author

martinbrook commented Feb 19, 2026

Master (HALF_REFRESH) Double FAST Delta
Page 1 total 2465ms 2105ms -360ms (15% faster)
Page 2 total 2449ms 2084ms -365ms (15% faster)
BW refresh wait 1583ms (half) 2 × 422ms = 844ms -739ms
Image renders 4× (same) 4× (same) same
Grayscale pass 62ms (same) 62ms (same) same

The double FAST approach is ~360ms faster per page because two FAST_REFRESHes (844ms) are faster than one HALF_REFRESH (1583ms). The extra BW render + fillRect for the image blanking adds back ~380ms of CPU work, but it's still a net win and the images are rendered correctly now.

@martinbrook martinbrook marked this pull request as ready for review February 19, 2026 23:10
Copy link
Member

@daveallie daveallie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well!

@daveallie daveallie merged commit 2a38bfd into crosspoint-reader:master Feb 20, 2026
8 checks passed
daveallie pushed a commit that referenced this pull request Feb 20, 2026
…957)

## Summary

Fixes #1011

Use double FAST_REFRESH for image pages to prevent grayscale washout,
HALF_REFRESH sets e-ink particles too firmly for the grayscale LUT to
adjust, causing washed-out images (especially large, light-gray ones).
Replace HALF_REFRESH with @pablohc's double FAST_REFRESH technique:
blank only the image bounding box area, then re-render with images. This
clears ghosting while keeping particles loosely set for grayscale.

## Additional Context

---

### 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? _**<  PARTIALLY  >**_
@pablohc
Copy link
Contributor

pablohc commented Feb 20, 2026

Thanks @martinbrook for this fix, and @ngxson for the idea! ;)

lukestein pushed a commit to lukestein/crosspoint-reader that referenced this pull request Feb 20, 2026
…rosspoint-reader#957)

## Summary

Fixes crosspoint-reader#1011

Use double FAST_REFRESH for image pages to prevent grayscale washout,
HALF_REFRESH sets e-ink particles too firmly for the grayscale LUT to
adjust, causing washed-out images (especially large, light-gray ones).
Replace HALF_REFRESH with @pablohc's double FAST_REFRESH technique:
blank only the image bounding box area, then re-render with images. This
clears ghosting while keeping particles loosely set for grayscale.

## Additional Context

---

### 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? _**<  PARTIALLY  >**_
@carsonwick
Copy link

Will this work for sleep screens as described in #627 ? Or is this another issue / choice?

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Large grayscale images in artemus epub are not rendered correctly

4 participants