Skip to content

Add cover image display in *Continue Reading* card with framebuffer caching#200

Merged
daveallie merged 6 commits intocrosspoint-reader:masterfrom
crosspoint-reader-ko:feature/continue-reading-cover
Jan 14, 2026
Merged

Add cover image display in *Continue Reading* card with framebuffer caching#200
daveallie merged 6 commits intocrosspoint-reader:masterfrom
crosspoint-reader-ko:feature/continue-reading-cover

Conversation

@eunchurn
Copy link
Contributor

@eunchurn eunchurn commented Jan 1, 2026

Summary

  • What is the goal of this PR? (e.g., Fixes a bug in the user authentication module,

Display the book cover image in the "Continue Reading" card on the home screen, with fast navigation using framebuffer caching.

  • What changes are included?
  • Display book cover image in the "Continue Reading" card on home screen
  • Load cover from cached BMP (same as sleep screen cover)
  • Add framebuffer store/restore functions (copyStoredBwBuffer, freeStoredBwBuffer) for fast navigation after initial render
  • Fix drawBitmap scaling bug: apply scale to offset only, not to base coordinates
  • Add white text boxes behind title/author/continue reading label for readability on cover
  • Support both EPUB and XTC file cover images
  • Increase HomeActivity task stack size from 2048 to 4096 for cover image rendering

Additional Context

  • Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on).
  • Performance: First render loads cover from SD card (~800ms), subsequent navigation uses cached framebuffer (~instant)
  • Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home screen, freed on exit
  • Fallback: If cover image is not available, falls back to standard text-only display
  • The drawBitmap fix corrects a bug where screenY = (y + offset) scale was incorrectly scaling the base coordinates. Now correctly uses screenY = y + (offset scale)

Display book cover image as background in the Continue Reading card on
the home screen, improving visual identification of the current book.

Key changes:
- Add thumbnail generation (thumb.bmp) for EPUB and XTC/XTCH files
  - Uses same dithering/scaling algorithms as sleep screen covers
  - Target size: 240x400 (half screen) for optimal Continue Reading card fit
- Add JpegToBmpConverter::jpegFileToBmpStreamWithSize() for custom target sizes
- Add GfxRenderer::copyStoredBwBuffer() and freeStoredBwBuffer() for
  framebuffer caching to maintain fast navigation performance
- Add UTF-8 safe string truncation for Korean/CJK text in title/author display
- Draw white boxes behind title/author text for readability over cover image
- Increase HomeActivityTask stack size to 4096 for cover image rendering
- Add bounds checking in XTC thumbnail generation to prevent buffer overflow
@eunchurn eunchurn force-pushed the feature/continue-reading-cover branch from 1179640 to 6fbdd06 Compare January 1, 2026 14:54
@lukestein
Copy link
Contributor

This PR hopefully closes #179

@daveallie
Copy link
Member

In order to keep the home screen rendering quick, I think we should be generating a 1-bit bmp to avoid having to do gray passes on the home screen. There's some existing implementation in this PR: #108

As it currently stands, this PR prepares a 2-bit bmp but renders all grays as black, so images are overly dark. Additionally, there seems to be an initialization issue as when I first land on the home screen, there's a bookmark icon on the cover, but after navigating down and up, it switched back to the expected border.

- Add 1-bit BMP generation with Atkinson dithering for better quality thumbnails
- Replace noise dithering with error diffusion for smoother gradients
- Add fillPolygon() to GfxRenderer for proper bookmark ribbon shape
- Change bookmark from rect+triangle carve to pentagon polygon
- Fix bookmark inversion when Continue Reading card is selected
- Show selection state on first render (not just after navigation)
- Fix 1-bit BMP palette lookup in Bitmap::readRow()
- Add drawBitmap1Bit() optimized path for 1-bit BMPs
The 1-bit format eliminates gray passes on home screen for faster rendering
while Atkinson dithering maintains good image quality through error diffusion.
@eunchurn
Copy link
Contributor Author

eunchurn commented Jan 4, 2026

In order to keep the home screen rendering quick, I think we should be generating a 1-bit bmp to avoid having to do gray passes on the home screen. There's some existing implementation in this PR: #108

As it currently stands, this PR prepares a 2-bit bmp but renders all grays as black, so images are overly dark. Additionally, there seems to be an initialization issue as when I first land on the home screen, there's a bookmark icon on the cover, but after navigating down and up, it switched back to the expected border.

I've addressed all the issues:

  1. 1-bit BMP with proper dithering

Changed from 2-bit to true 1-bit BMP format for thumbnails
Implemented Atkinson dithering (error diffusion) instead of simple thresholding
The algorithm: resize to target size (240x400) first, then apply Atkinson dithering
This produces much better quality than the previous approach where grays were rendered as black

  1. Bookmark ribbon fix

Replaced the rect + triangle "carve out" approach with a proper polygon (pentagon shape)
Added fillPolygon() method to GfxRenderer using scanline fill algorithm
The bookmark now renders correctly as a ribbon with a V-notch at the bottom

  1. Initialization issue fixed

Selection state (border + inverted bookmark) is now shown on first render, not just after navigation
The bookmark inverts properly when the Continue Reading card is selected

  1. Additional improvements

Fixed 1-bit BMP palette lookup in Bitmap::readRow()
Added optimized drawBitmap1Bit() rendering path
XTC thumbnails also updated to use 1-bit format with noise dithering

The home screen now renders without gray passes while maintaining good image quality through Atkinson error diffusion dithering.

Comment on lines +274 to +287
// Draw bookmark ribbon (white normally, will be inverted if selected)
renderer.fillPolygon(xPoints, yPoints, 5, false);

// Store the buffer with cover image AND bookmark for fast navigation
coverBufferStored = renderer.storeBwBuffer();
coverRendered = true;

// First render: if selected, draw selection indicators now
if (bookSelected) {
renderer.drawRect(bookX + 1, bookY + 1, bookWidth - 2, bookHeight - 2);
renderer.drawRect(bookX + 2, bookY + 2, bookWidth - 4, bookHeight - 4);
// Invert bookmark to black
renderer.fillPolygon(xPoints, yPoints, 5, true);
}
Copy link
Member

Choose a reason for hiding this comment

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

Sorry maybe the feedback was bad, but we want to be doing the opposite here, if we've shown a cover, we can omit the bookmark icon as it's not adding anything and just covering the art. If there's no cover, then we should show the bookmark as an additional visual flair.

Currently we show the bookmark icon when there is a cover, and show nothing when there's no cover.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! I've inverted the logic as requested. Now the bookmark ribbon is only shown when there's no cover image (as visual decoration), and hidden when a cover is displayed so it doesn't obscure the artwork. The bookmark also properly inverts when selected in the no-cover case.

renderer.fillPolygon(xPoints, yPoints, 5, false);

// Store the buffer with cover image AND bookmark for fast navigation
coverBufferStored = renderer.storeBwBuffer();
Copy link
Member

Choose a reason for hiding this comment

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

I think this is a bit of a bastardisation of the storeBwBuffer function. That function was designed to temporarily hold the BW buffer for gray renders, we're using it to store a full screen frame atm.

If we'd like to store a full screen frame, then we should make it HomeActivity's responsibility to store that frame. When constructing HomeActivity we should be building our own buffer and cloning the frame buffer into it, something like:

// During initialization / entering activity:
auto baseFrame = static_cast<uint8_t *>(malloc(renderer.getBufferSize()));

// Instead of the this storeBwBuffer call
memcpy(baseFrame, renderer.getFrameBuffer(), renderer.getBufferSize());

// When restoring it
memcpy(renderer.getFrameBuffer(), baseFrame, renderer.getBufferSize());

// When exiting
free(baseFrame);

Alternatively, we could just copy a subregion of the frame buffer where the cover is (which is only 1/4 of the screen) and then display that back onto the screen when we need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've refactored this to use HomeActivity's own buffer management. Added three private methods: storeCoverBuffer(), restoreCoverBuffer(), and freeCoverBuffer() that handle a dedicated uint8_t* coverBuffer member. This keeps the buffer responsibility within HomeActivity and leaves storeBwBuffer/restoreBwBuffer for their intended grayscale rendering purpose.

Comment on lines +580 to +611
/**
* Copy stored BW buffer to framebuffer without freeing the stored chunks.
* Use this when you want to restore the buffer but keep it for later reuse.
* Returns true if buffer was copied successfully.
*/
bool GfxRenderer::copyStoredBwBuffer() {
// Check if all chunks are allocated
for (const auto& bwBufferChunk : bwBufferChunks) {
if (!bwBufferChunk) {
return false;
}
}

uint8_t* frameBuffer = einkDisplay.getFrameBuffer();
if (!frameBuffer) {
return false;
}

for (size_t i = 0; i < BW_BUFFER_NUM_CHUNKS; i++) {
const size_t offset = i * BW_BUFFER_CHUNK_SIZE;
memcpy(frameBuffer + offset, bwBufferChunks[i], BW_BUFFER_CHUNK_SIZE);
}

return true;
}

/**
* Free the stored BW buffer chunks manually.
* Use this when you no longer need the stored buffer.
*/
void GfxRenderer::freeStoredBwBuffer() { freeBwBufferChunks(); }

Copy link
Member

Choose a reason for hiding this comment

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

Given some of the feedback in the other files, I think we should remove this functionality as it opens up a little too much room for abuse for my liking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I've removed both copyStoredBwBuffer() and freeStoredBwBuffer() from GfxRenderer. The original storeBwBuffer() and restoreBwBuffer() are kept since they're still used by EpubReaderActivity for grayscale rendering as intended.

free(rowBytes);
}

void GfxRenderer::fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state) const {
Copy link
Member

Choose a reason for hiding this comment

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

Nice add, thanks!

Comment on lines +27 to +46
// UTF-8 safe string truncation - removes one character from the end
// Returns the new size after removing one UTF-8 character
size_t utf8RemoveLastChar(std::string& str) {
if (str.empty()) return 0;
size_t pos = str.size() - 1;
// Walk back to find the start of the last UTF-8 character
// UTF-8 continuation bytes start with 10xxxxxx (0x80-0xBF)
while (pos > 0 && (static_cast<unsigned char>(str[pos]) & 0xC0) == 0x80) {
--pos;
}
str.resize(pos);
return pos;
}

// Truncate string by removing N UTF-8 characters from the end
void utf8TruncateChars(std::string& str, size_t numChars) {
for (size_t i = 0; i < numChars && !str.empty(); ++i) {
utf8RemoveLastChar(str);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

For now these can stay here, but will relocate them once StringUtils get merged in from the Calibre Syncing PR

- Invert bookmark icon logic: show bookmark only when no cover image
  (as visual decoration), hide when cover is displayed to show more art
- Replace GfxRenderer::storeBwBuffer usage with HomeActivity's own
  buffer management (storeCoverBuffer/restoreCoverBuffer/freeCoverBuffer)
- Remove copyStoredBwBuffer() and freeStoredBwBuffer() from GfxRenderer
  as they enabled misuse of the grayscale buffer storage mechanism
Resolve conflicts:
- GfxRenderer: Add cropX/cropY params to drawBitmap, keep 1-bit BMP support
- GfxRenderer: Update drawBitmap1Bit to use readNextRow (API change)
- JpegToBmpConverter: Use upstream scaling logic (larger dimension)
- HomeActivity: Use StringUtils::checkFileExtension, add hasOpdsUrl
- HomeActivity: Keep cover image functionality with own buffer management
@eunchurn eunchurn requested a review from daveallie January 9, 2026 15:03
@daveallie
Copy link
Member

Apologies, I've created more merge conflicts with the merge of #302, if you could resolve them, then I can get this in.

Resolved merge conflicts:
- lib/Epub/Epub.h: Kept both upstream's cropped parameter and HEAD's thumb functions
- lib/JpegToBmpConverter/JpegToBmpConverter.cpp: Used upstream's version (dithering code moved to BitmapHelpers)
- src/activities/home/HomeActivity.cpp: Kept HEAD's comment about stack size

Added missing 1-bit dithering support to BitmapHelpers:
- Added Atkinson1BitDitherer class to BitmapHelpers.h
- Added quantize1bit() function to BitmapHelpers.cpp
@daveallie daveallie merged commit fecd184 into crosspoint-reader:master Jan 14, 2026
1 check passed
@itsthisjustin
Copy link
Contributor

Fwiw I'd recommend that if the cover image is available that we hide the book name and author since that's usually on the book cover as well

@lukestein
Copy link
Contributor

Fwiw I'd recommend that if the cover image is available that we hide the book name and author since that's usually on the book cover as well

Respectfully disagree. This info can be small or hard to read in the thumbnail depending on the book.

I do think we could use a smaller font in the overlay.

And the overlay should invert colors (white text on black background) when selected, consistent with the other buttons on the home screen.

@itsthisjustin
Copy link
Contributor

Fwiw I'd recommend that if the cover image is available that we hide the book name and author since that's usually on the book cover as well

Respectfully disagree. This info can be small or hard to read in the thumbnail depending on the book.

I do think we could use a smaller font in the overlay.

And the overlay should invert colors (white text on black background) when selected, consistent with the other buttons on the home screen.

Well, what does Kindle or others do? I've never seen a reader UI that has both the cover and the book title stacked on top of each other haha. To me, it looks like a bug or a modal with information i need to dismiss. I'm fine with it being there in addition to the cover but why have the cover if you're gonna cover it with a box? defeats the purpose of having a nice beautiful book cover at all

@pablohc
Copy link
Contributor

pablohc commented Jan 16, 2026

Fwiw I'd recommend that if the cover image is available that we hide the book name and author since that's usually on the book cover as well

I second this. You're already supposed to know what book you're reading; you recognize the art, and there wouldn't be an issue with slightly less legible text in the art. Adding the current book's title label ruins the experience.

If book covers were shown in a grid instead of a list of titles, displaying the name below the cover would make sense, especially when you haven’t opened the book yet and the text is hard to read.
But overlaying the title on the cover adds nothing, neither in the current "Continue Reading" view nor in a future cover grid (if it ever happens).

@eunchurn Is it possible to modify this?

I've also been thinking that if the cover image is going to be scaled to fit the width of the container, vertical alignment might not be necessary. It could be aligned to the bottom, which would avoid the white boxes that currently appear above and below the covers.
I apologize if what I'm saying isn't clear; I could create a mockup to better illustrate the idea, in case the modification is feasible.

yingirene pushed a commit to yingirene/crosspoint-reader that referenced this pull request Jan 16, 2026
…aching (crosspoint-reader#200)

## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,

Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.

* **What changes are included?**

- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).

- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
alpsfordays added a commit to alpsfordays/crosspoint-reader-fr that referenced this pull request Jan 16, 2026
commit 21277e0
Author: Luke Stein <[email protected]>
Date:   Thu Jan 15 07:27:17 2026 -0500

    docs: Update User Guide to reflect release 0.14.0 (crosspoint-reader#376)

commit 4eef2b5
Author: Luke Stein <[email protected]>
Date:   Thu Jan 15 07:26:39 2026 -0500

    feat: Add MAC address display to WiFi Networks screen (crosspoint-reader#381)

    ## Summary

    * Implements crosspoint-reader#380, allowing the user to see the device's MAC address in
    order to register on wifi networks

    ## Additional Context

    * Although @markatlnk suggested showing on the settings screen, I
    implemented display at the bottom of the WiFi Networks selection screen
    (accessed via "File Transfer" > "Join a Network") since I think it makes
    more sense there.
    * Tested on my own device

    ![IMG_2873](https://github.com/user-attachments/assets/b82a20dc-41a0-4b21-81f1-20876aa2c6b0)

    ---

    ### 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? _**YES**_

    ---------

    Co-authored-by: copilot-swe-agent[bot] <[email protected]>

commit 5a55fa1
Author: Nathan James <[email protected]>
Date:   Thu Jan 15 12:25:18 2026 +0000

    fix: also apply longPressChapterSkip setting to xtc reader (crosspoint-reader#378)

    ## Summary

    * This builds upon the helpful PR
    crosspoint-reader#341, and
    adds support for the setting to also apply to the XTC reader, which I
    believed has just been missed and was not intentionally left out.
    * XTC does not have chapter support yet, but it does skip 10 pages when
    long-pressed, and so I think this is useful.

    ---

    ### AI Usage

    Did you use AI tools to help write this code? No

commit c98ba14
Author: Maeve Andrews <[email protected]>
Date:   Thu Jan 15 06:23:36 2026 -0600

    fix: draw button hints correctly if orientation is not portrait (crosspoint-reader#363)

    ~~Quick~~ fix for
    crosspoint-reader#362

    (this just applies to the chapter selection menu:)

    ~~If the orientation is portrait, hints as we know them make sense to
    draw. If the orientation is inverted, we'd have to change the order of
    the labels (along with everything's position), and if it's one of the
    landscape choices, we'd have to render the text and buttons vertically.
    All those other cases will be more complicated.~~

    ~~Punt on this for now by only rendering if portrait.~~

    Update: this now draws the hints at the physical button position no
    matter what the orientation is, by temporarily changing orientation to
    portrait.

    ---------

    Co-authored-by: Maeve Andrews <[email protected]>

commit c1c94c0
Author: Jonas Diemer <[email protected]>
Date:   Thu Jan 15 13:21:46 2026 +0100

    Feature: Show img alt text (crosspoint-reader#168)

    Let's start small by showing the ALT text of IMG. This is rudimentary,
    but avoids those otherwise completely blank chapters.

    I feel we will need this even when we can render images if that
    rendering takes >1s - I would then prefer rendering optional and showing
    the ALT text first.

commit eb84bce
Author: Dave Allie <[email protected]>
Date:   Thu Jan 15 02:22:38 2026 +1100

    chore: Pin links2004/WebSockets version

commit d45f355
Author: efenner <[email protected]>
Date:   Thu Jan 15 05:14:59 2026 -0700

    feat: Add EPUB table omitted placeholder (crosspoint-reader#372)

    ## Summary

    * **What is the goal of this PR?**: Fix the bug I reported in
    crosspoint-reader#292
    * **What changes are included?**: Instead of silently dropping table
    content in EPUBs., replace with an italicized '[Table omitted]' message
    where tables appear.

    ## Additional Context

    * Add any other information that might be helpful for the reviewer
    (e.g., performance implications, potential risks,
      specific areas to focus on).

    ---

    ### 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 **_

    ---------

    Co-authored-by: Evan Fenner <[email protected]>
    Co-authored-by: Warp <[email protected]>

commit 56ec3df
Author: Dave Allie <[email protected]>
Date:   Thu Jan 15 01:01:47 2026 +1100

    chore: Cut release 0.14.0

commit e517945
Author: Maeve Andrews <[email protected]>
Date:   Wed Jan 14 06:23:03 2026 -0600

    Add a bullet to the beginning of any <li> (crosspoint-reader#368)

    Currently there is no visual indication whatsoever if something is in a
    list. An `<li>` is essentially just another paragraph.

    As a partial remedy for this, add a bullet character to the beginning of
    `<li>` text blocks so that the user can see that they're list items.
    This is incomplete in that an `<ol>` should also have a counter so that
    its list items can get numbers instead of bullets (right now I don't
    think we track if we're in a `<ul>` or an `<ol>` at all), but it's
    strictly better than the current situation.

    Co-authored-by: Maeve Andrews <[email protected]>

commit 4892208
Author: Maeve Andrews <[email protected]>
Date:   Wed Jan 14 06:21:48 2026 -0600

    Only indent paragraphs for justify/left-align (crosspoint-reader#367)

    Currently, when Extra Paragraph Spacing is off, an em-space is added to
    the beginning of each ParsedText even for blocks like headers that are
    centered. This whitespace makes the centering slightly off. Change the
    calculation here to only add the em-space for left/justified text.

    Co-authored-by: Maeve Andrews <[email protected]>

commit 3ee10b3
Author: Dave Allie <[email protected]>
Date:   Wed Jan 14 23:14:00 2026 +1100

    Update OTA updater URL

commit a946c83
Author: swwilshub <[email protected]>
Date:   Wed Jan 14 12:11:28 2026 +0000

    Turbocharge WiFi uploads with WebSocket + watchdog stability (crosspoint-reader#364)

    ## Summary

    * **What is the goal of this PR?** Fix WiFi file transfer stability
    issues (especially crashes during uploads) and improve upload speed via
    WebSocket binary protocol. File transfers now don't really crash as
    much, if they do it recovers and speed has gone form 50KB/s to 300+KB/s.

    * **What changes are included?**
    - **WebSocket upload support** - Adds WebSocket binary protocol for file
    uploads, achieving faster speeds 335 KB/s vs HTTP multipart)
    - **Watchdog stability fixes** - Adds `esp_task_wdt_reset()` calls
    throughout upload path to prevent watchdog timeouts during:
        - File creation (FAT allocation can be slow)
        - SD card write operations
        - HTTP header parsing
        - WebSocket chunk processing
    - **4KB write buffering** - Batches SD card writes to reduce I/O
    overhead
    - **WiFi health monitoring** - Detects WiFi disconnection in STA mode
    and exits gracefully
    - **Improved handleClient loop** - 500 iterations with periodic watchdog
    resets and button checks for responsiveness
    - **Progress bar improvements** - Fixed jumping/inaccurate progress by
    capping local progress at 95% until server confirms completion
    - **Exit button responsiveness** - Button now checked inside the
    handleClient loop every 64 iterations
    - **Reduced exit delays** - Decreased shutdown delays from ~850ms to
    ~140ms

    **Files changed:**
    - `platformio.ini` - Added WebSockets library dependency
    - `CrossPointWebServer.cpp/h` - WebSocket server, upload buffering,
    watchdog resets
    - `CrossPointWebServerActivity.cpp` - WiFi monitoring, improved loop,
    button handling
    - `FilesPage.html` - WebSocket upload JavaScript with HTTP fallback

    ## Additional Context

    - WebSocket uses 4KB chunks with backpressure management to prevent
    ESP32 buffer overflow
    - Falls back to HTTP automatically if WebSocket connection fails
    - The main bottleneck now is SD card write speed (~44% of transfer
    time), not WiFi
    - STA mode was more prone to crashes than AP mode due to external
    network factors; WiFi health monitoring helps detect and handle
    disconnections gracefully

    ---

    ### AI Usage

    Did you use AI tools to help write this code? _**YES**_ Claude did it
    ALL, I have no idea what I am doing, but my books transfer fast now.

    ---------

    Co-authored-by: Claude <[email protected]>

commit 847786e
Author: Justin Mitchell <[email protected]>
Date:   Wed Jan 14 06:54:14 2026 -0500

    Fixes issue with Calibre web expecting SSL (crosspoint-reader#347)

    http urls now work with Calibre web

    ---------

    Co-authored-by: Dave Allie <[email protected]>

commit c2fb8ce
Author: Luke Stein <[email protected]>
Date:   Wed Jan 14 06:48:43 2026 -0500

    Update User Guide to reflect release 0.13.1 (crosspoint-reader#337)

    Please note I have not tested the Calibre features and am not yet in a
    position to offer detailed documentation of how they work.

commit ed05554
Author: Armando Cerna <[email protected]>
Date:   Wed Jan 14 06:47:24 2026 -0500

    feat: Add setting to toggle long-press chapter skip (crosspoint-reader#341)

    ## Summary

    Adds a new "Long-press Chapter Skip" toggle in Settings to control
    whether holding the side buttons skips chapters.

    I kept accidentally triggering chapter skips while reading, which caused
    me to lose my place in the middle of long chapters.

    ## Additional Context

    * Add any other information that might be helpful for the reviewer
    (e.g., performance implications, potential risks,
      specific areas to focus on).

    ---

    ### 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 **_

commit 9a9dc04
Author: Jonas Diemer <[email protected]>
Date:   Wed Jan 14 12:40:40 2026 +0100

    ifdef around optional fonts to reduce flash size/time. (crosspoint-reader#339)

    ## Summary

    Adds define to omit optional fonts from the build. This reduces time to
    flash from >31s to <13s. Useful for development that doesn't require
    fonts. Addresses crosspoint-reader#193

    Invoke it like this during development:
    `PLATFORMIO_BUILD_FLAGS="-D OMIT_FONTS" pio run --target upload && pio
    device monitor`

    Changing the define causes `pio` to do a full rebuild (but it will be
    quick if you keep the define).

    ---

    ### 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? NO

commit 1c027ce
Author: Jonas Diemer <[email protected]>
Date:   Wed Jan 14 12:38:30 2026 +0100

    Skip BOM character (sometimes used in front of em-dashes) (crosspoint-reader#340)

    ## Summary

    Skip BOM character (sometimes used in front of em-dashes) - they are not
    part of the glyph set and would render `?` otherwise.

    ---

    ### AI Usage

    Did you use AI tools to help write this code? _**YES**_

commit 49f97b6
Author: Eunchurn Park <[email protected]>
Date:   Wed Jan 14 19:36:40 2026 +0900

    Add TXT file reader support (crosspoint-reader#240)

    ## Summary

    * **What is the goal of this PR?**

    Add support for reading plain text (.txt) files, enabling users to
    browse, read, and track progress in TXT documents alongside existing
    EPUB and XTC formats.

    * **What changes are included?**

    - New Txt library for loading and parsing plain text files
    - New TxtReaderActivity with streaming page rendering using 8KB chunks
    to handle large files without memory issues on ESP32-C3
    - Page index caching system (index.bin) for instant re-open after sleep
    or app restart
    - Progress bar UI during initial file indexing (matching EPUB style)
    - Word wrapping with proper UTF-8 support
    - Cover image support for TXT files:
    - Primary: image with same filename as TXT (e.g., book.jpg for book.txt)
      - Fallback: cover.bmp/jpg/jpeg in the same folder
      - JPG to BMP conversion using existing converter
      - Sleep screen cover mode now works with TXT files
    - File browser now shows .txt files

    ## Additional Context

    * Add any other information that might be helpful for the reviewer

    * Memory constraints: The streaming approach was necessary because
    ESP32-C3 only has 320KB RAM. A 700KB TXT file cannot be loaded entirely
    into memory, so we read 8KB chunks and build a page offset index
    instead.

    * Cache invalidation: The page index cache automatically invalidates
    when file size, viewport width, or lines per page changes (e.g., font
    size or orientation change).

    * Performance: First open requires indexing (with progress bar),
    subsequent opens load from cache instantly.

    * Cover image format: PNG is detected but not supported for conversion
    (no PNG decoder available). Only BMP and JPG/JPEG work.

commit 14643d0
Author: Dave Allie <[email protected]>
Date:   Wed Jan 14 21:19:12 2026 +1100

    Move string helpers out of HomeActivity into StringUtils

commit fecd184
Author: Eunchurn Park <[email protected]>
Date:   Wed Jan 14 19:24:02 2026 +0900

    Add cover image display in *Continue Reading* card with framebuffer caching (crosspoint-reader#200)

    ## Summary

    * **What is the goal of this PR?** (e.g., Fixes a bug in the user
    authentication module,

    Display the book cover image in the **"Continue Reading"** card on the
    home screen, with fast navigation using framebuffer caching.

    * **What changes are included?**

    - Display book cover image in the "Continue Reading" card on home screen
    - Load cover from cached BMP (same as sleep screen cover)
    - Add framebuffer store/restore functions (`copyStoredBwBuffer`,
    `freeStoredBwBuffer`) for fast navigation after initial render
    - Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
    coordinates
    - Add white text boxes behind title/author/continue reading label for
    readability on cover
    - Support both EPUB and XTC file cover images
    - Increase HomeActivity task stack size from 2048 to 4096 for cover
    image rendering

    ## Additional Context

    * Add any other information that might be helpful for the reviewer
    (e.g., performance implications, potential risks, specific areas to
    focus on).

    - Performance: First render loads cover from SD card (~800ms),
    subsequent navigation uses cached framebuffer (~instant)
    - Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
    screen, freed on exit
    - Fallback: If cover image is not available, falls back to standard
    text-only display
    - The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
    was incorrectly scaling the base coordinates. Now correctly uses screenY
    = y + (offset scale)

commit 2040e08
Author: Will Morrow <[email protected]>
Date:   Wed Jan 14 05:05:08 2026 -0500

    Ensure new custom sleep image every time (crosspoint-reader#300)

    When picking a random sleep image from a set of custom images, compare
    the randomly chosen index against a cached value in settings. If the
    value matches, use the next image (rolling over if it's the last image).
    Cache the chosen image index to settings in either case.

    ## Summary

    Implements a tweak on the custom sleep image feature that ensures that
    the user gets a new image every time the device goes to sleep.
    This change adds a new setting (perhaps there's a better place to cache
    this?) that stores the most recently used file index. During picking the
    random image index, we compare this against the random index and choose
    the next one (modulo the number of image files) if it matches, ensuring
    we get a new image.

    ## Additional Context

    As mentioned, I used settings to cache this value since it is a
    persisted store, perhaps that's overkill. Open to suggestions on if
    there's a better way.

commit 65d2391
Author: Andrew Brandt <[email protected]>
Date:   Wed Jan 14 04:04:02 2026 -0600

    ci: add PR format check workflow (crosspoint-reader#328)

    **Description**:

    Add a new workflow to check the PR formatting to ensure consistency on
    PR titles. We can also use this for semantic release versioning later,
    if we so desire.

    **Related Issue(s)**:

    Implements first portion of crosspoint-reader#327

    ---------

    Signed-off-by: Andrew Brandt <[email protected]>

commit 52995fa
Author: Dave Allie <[email protected]>
Date:   Tue Jan 13 02:09:39 2026 +1100

    chore: Cut release 0.13.1

commit d4f8eda
Author: Dave Allie <[email protected]>
Date:   Tue Jan 13 01:09:06 2026 +1000

    fix: Increase home activity stack size (crosspoint-reader#333)

    ## Summary

    * fix: Increase home activity stack size

    ## Additional Context

    * Home activity can crash occasionally depending on book

    ---

    ### 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? No

commit 33b8fa0
Author: Dave Allie <[email protected]>
Date:   Tue Jan 13 00:56:21 2026 +1100

    chore: Cut release 0.13.0

commit 16c760b
Author: Dave Allie <[email protected]>
Date:   Tue Jan 13 00:58:43 2026 +1100

    copy: Tweak pull request template wording

commit 8f3df7e
Author: Dave Allie <[email protected]>
Date:   Mon Jan 12 23:57:34 2026 +1000

    fix: Handle EPUB 3 TOC to spine mapping when nav file in subdirectory (crosspoint-reader#332)

    ## Summary

    - Nav file in EPUB 3 file is a HTML file with relative hrefs
    - If this file exists anywhere but in the same location as the
    content.opf file, navigating in the book will fail
    - Bump the book cache version to rebuild potentially broken books

    ## Additional Context

    - Fixes crosspoint-reader#264

    ---

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

    - [ ] Yes
    - [ ] Partially
    - [x] No

commit 0165fab
Author: Jonas Diemer <[email protected]>
Date:   Mon Jan 12 12:36:19 2026 +0100

    Fix BMP rendering gamma/brightness (crosspoint-reader#302)

    1. Refactor Bitmap.cpp/h to expose the options for FloydSteinberg and
    brightness/gamma correction at runtime
    2. Fine-tune the thresholds for Floyd Steiberg and simple quantization
    to better match the display's colors

    Turns out that 2 is enough to make the images render properly, so the
    brightness boost and gamma adjustment doesn't seem necessary currently
    (at least for my test image).

commit 66b100c
Author: danoob <[email protected]>
Date:   Mon Jan 12 17:49:42 2026 +0700

    fix: Wi-Fi Selection on Calibre Library launch (crosspoint-reader#313)

    ## Summary

    * **What is the goal of this PR?**
    Fixes the Wi-Fi connection issue when launching the Calibre Library
    (OPDS browser). The previous implementation always attempted to connect
    using the first saved WiFi credential, which caused connection failures
    when users were in locations where only other saved networks (not the
    first one) were available. Now, the activity launches a WiFi selection
    screen allowing users to choose from available networks.

    * **What changes are included?**

    ## Additional Context
    **Bug Fixed**: Previously, the code used `credentials[0]` (always the
    first saved WiFi), so users in areas with only their secondary/tertiary
    saved networks available could never connect.

    ---------

    Co-authored-by: danoooob <[email protected]>

commit 41bda43
Author: Andrew Brandt <[email protected]>
Date:   Mon Jan 12 04:37:23 2026 -0600

    chore: update formatting in github workflows (crosspoint-reader#320)

    **Description**:

    The purpose of this change is to modify the spacing in the
    `.github/workflow` files to ensure consistency.

    **Related Issue(s)**:

    Implements crosspoint-reader#319

    Signed-off-by: Andrew Brandt <[email protected]>

commit 82f21f3
Author: Dave Allie <[email protected]>
Date:   Mon Jan 12 21:35:18 2026 +1100

    Add AI usage question to the PR template

commit a9242fe
Author: Jonas Diemer <[email protected]>
Date:   Mon Jan 12 10:55:47 2026 +0100

    Generate different .bmp for cropped covers so settings have effect. (crosspoint-reader#330)

    Addresses
    crosspoint-reader#225 (comment)

commit 88d0d90
Author: Jonas Diemer <[email protected]>
Date:   Mon Jan 12 10:53:58 2026 +0100

    Add option to hide battery percentage. (crosspoint-reader#297)

    with option to always hide or hide in reader only.

    Co-authored-by: Dave Allie <[email protected]>

commit 97c4871
Author: David Fischer <[email protected]>
Date:   Mon Jan 12 10:07:26 2026 +0100

    Add page turn on power button press (crosspoint-reader#286)

    ## Summary

    * **What is the goal of this PR?**
    * This PR adds a setting to (additionally) map the forward page turn
    onto the powerbutton when in `EPUBReaderActivity` and powerbutton short
    press is not mapped to sleep mode. I find the powerbutton to be exactly
    where my thumb is while reading so it is very convenient to map the
    forwardpage turn to that. Maybe Im not alone with this ^^
    * **What changes are included?**

    ## Additional Context

    * Add any other information that might be helpful for the reviewer
    (e.g., performance implications, potential risks, specific areas to
    focus on).

commit 66811bf
Author: Seth <[email protected]>
Date:   Mon Jan 12 00:59:02 2026 -0800

    Add navigation hints to ChapterSelectionActivities (crosspoint-reader#294)

    ## Summary

    Add navigation hints to Chapter Select - crosspoint-reader#190

    ### Before

    ![Mi 11X_20260108_214114_lmc_8
    4](https://github.com/user-attachments/assets/45031d21-2c6c-4b7d-a5cc-6ad111bf5a70)

    ### After
    ![Mi 11X_20260108_213803_lmc_8
    4](https://github.com/user-attachments/assets/1fa4ef22-63e4-4adb-8fc5-5fb8c7fa79fa)

commit 8728701
Author: Samuel Carpentier <[email protected]>
Date:   Mon Jan 12 17:58:08 2026 +0900

    Updated user guide (sleep screens list) (crosspoint-reader#293)

    ## Summary

    Updated sleep screens list by adding the newly available "Blank" option
@daveallie
Copy link
Member

daveallie commented Jan 19, 2026

I am hesitant to completely remove the title when the book cover is being shown, perhaps we can show it below the cover to avoid occluding it, but I have a few books where the "cover" is just the Penguin Books logo or something equally silly.

Perhaps that's just a library management issue though

swwilshub pushed a commit to swwilshub/crosspoint-reader-speedup that referenced this pull request Jan 19, 2026
…aching (crosspoint-reader#200)

## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,

Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.

* **What changes are included?**

- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).

- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
Unintendedsideeffects pushed a commit to Unintendedsideeffects/crosspoint-reader that referenced this pull request Feb 17, 2026
…aching (crosspoint-reader#200)

## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,

Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.

* **What changes are included?**

- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).

- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
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.

5 participants