Skip to content

feat: Added BmpViewer activity for viewing .bmp images in file browser#887

Merged
osteotek merged 19 commits intocrosspoint-reader:masterfrom
Levrk:BmpViewer
Feb 21, 2026
Merged

feat: Added BmpViewer activity for viewing .bmp images in file browser#887
osteotek merged 19 commits intocrosspoint-reader:masterfrom
Levrk:BmpViewer

Conversation

@Levrk
Copy link
Contributor

@Levrk Levrk commented Feb 15, 2026

Summary

  • What is the goal of this PR? (e.g., Implements the new feature for file uploading.)

Implements new feature for viewing .bmp files directly from the "Browse Files" menu.

  • What changes are included?

You can now view .bmp files when browsing. You can click the select button to open the file, and then click back to close it and continue browsing in the same location. Once open a file will display on the screen with no additional options to interact outside of exiting with the back button.

The attached video shows this feature in action:

Untitled.mov

Additional Context

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

The changes implemented in #884 are also present here as this feature is actually what led to me noticing this issue. I figured I would add that PR as a separate request in case that one could be more easily merged given this feature is significantly more complicated and will likely be subject to more intense review.


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

Levrk and others added 5 commits February 14, 2026 16:35
Empty Button Icons (Back button in the home menu) were still rendering the full sized white rectangles. This was not visible on the home screen due to the white background.
Added basic functionality for opening and closing bmp files from the library browser requiring UI update from menu-bar-fix to improve aesthetics.
Fixed Empty Button Icon Rendering
Fixed the back button in the BmpViewer app to return to the library instead of all the way home.
Fixed formating with ./bin/clang-format-fix
@Levrk
Copy link
Contributor Author

Levrk commented Feb 15, 2026

I hope this counts as being in scope. I think being able to just view a .bmp is good for choosing wallpapers. I also think it is strange that when viewing the sleep folder in the file browser it shows as completely empty. I think the baseline ability to view an image file on demand instead of having to set it as a wallpaper and turn the device off is well within the idealogical bounds of Library Management and User Experience.

Happy to take any critiques on optimizing performance or better fitting in with existing directory structure. I did consider putting the activity in the reader directory, but ultimately decided that it functions more as a util than anything else.

Levrk and others added 2 commits February 15, 2026 14:07
Buttons are now updated if they have been remapped and added « to keep consistent
@pablohc
Copy link
Contributor

pablohc commented Feb 16, 2026

@Levrk You could move to the next or previous image once one of them is open?

Copy link
Contributor

@CaptainFrito CaptainFrito left a comment

Choose a reason for hiding this comment

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

Good feature idea, this has been missing since the beginning!

Fixed button fills to be rounded
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds BMP support: library now includes .bmp files; ReaderActivity detects and routes BMPs to a new BmpViewerActivity; new activity opens, parses, and renders BMP files with back navigation; Lyra theme button hints now use rounded rectangles.

Changes

Cohort / File(s) Summary
Library & Reader integration
src/activities/home/MyLibraryActivity.cpp, src/activities/reader/ReaderActivity.h, src/activities/reader/ReaderActivity.cpp
Library file filter extended to include .bmp. ReaderActivity adds isBmpFile() and onGoToBmpViewer() and routes BMP paths to the new BMP viewer in its entry flow.
BMP Viewer activity
src/activities/util/BmpViewerActivity.h, src/activities/util/BmpViewerActivity.cpp
New BmpViewerActivity class and implementation. Constructor (GfxRenderer&, MappedInputManager&, std::string, std::function<void()>). Implements onEnter, onExit, loop; opens BMP via Storage, parses headers, renders centered bitmap, displays button hints, handles Back and error states.
Theme rendering
src/components/themes/lyra/LyraTheme.cpp
Button hint backgrounds for FULL and SMALL sizes changed from filled rectangles to filled rounded rectangles (fillRoundedRect).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ReaderActivity
    participant BmpViewerActivity
    participant Storage
    participant GfxRenderer
    participant MappedInputManager

    User->>ReaderActivity: open(path)
    ReaderActivity->>ReaderActivity: isBmpFile(path)?
    alt BMP file
        ReaderActivity->>BmpViewerActivity: instantiate(path, onGoBack)
        ReaderActivity->>BmpViewerActivity: onEnter()
        BmpViewerActivity->>Storage: open(path)
        Storage-->>BmpViewerActivity: file stream / error
        BmpViewerActivity->>BmpViewerActivity: parse BMP
        BmpViewerActivity->>GfxRenderer: render centered bitmap
        GfxRenderer-->>BmpViewerActivity: draw complete
        BmpViewerActivity->>MappedInputManager: show button hints
        User->>BmpViewerActivity: press Back
        BmpViewerActivity->>BmpViewerActivity: loop() detects Back
        BmpViewerActivity->>BmpViewerActivity: onExit()
        BmpViewerActivity->>ReaderActivity: onGoBack()
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main feature addition: BMP viewer activity for viewing .bmp images in the file browser, which matches the core changes across all modified files.
Description check ✅ Passed The description clearly explains the feature goal, implementation details, usage flow, and provides context about related changes and methodology, all directly related to the changeset.

✏️ 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.

@Levrk
Copy link
Contributor Author

Levrk commented Feb 17, 2026

Good catch @CaptainFrito! added rounded corner fills. Here is a screenshot where you can see that clearly:

20260217_072124

@Levrk Levrk requested a review from CaptainFrito February 17, 2026 12:27
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: 2

🧹 Nitpick comments (3)
src/activities/util/BmpViewerActivity.cpp (2)

26-33: Large images are clipped rather than scaled.

When the BMP dimensions exceed screen size, the image is drawn at its original size and clipped. This may cut off parts of larger images. If full visibility is desired, consider scaling the image to fit within screen bounds while maintaining aspect ratio.

💡 Optional: Scale large images to fit
       // Calculate centering
-      int32_t xOffset = (renderer.getScreenWidth() - bmpW) / 2;
-      int32_t yOffset = (renderer.getScreenHeight() - bmpH) / 2;
-
-      if (xOffset < 0) xOffset = 0;
-      if (yOffset < 0) yOffset = 0;
-
-      renderer.drawBitmap(bmp, xOffset, yOffset, bmpW, bmpH);
+      int32_t screenW = renderer.getScreenWidth();
+      int32_t screenH = renderer.getScreenHeight();
+      int32_t drawW = bmpW;
+      int32_t drawH = bmpH;
+
+      // Scale down if image exceeds screen dimensions
+      if (bmpW > screenW || bmpH > screenH) {
+        float scaleX = static_cast<float>(screenW) / bmpW;
+        float scaleY = static_cast<float>(screenH) / bmpH;
+        float scale = std::min(scaleX, scaleY);
+        drawW = static_cast<int32_t>(bmpW * scale);
+        drawH = static_cast<int32_t>(bmpH * scale);
+      }
+
+      int32_t xOffset = (screenW - drawW) / 2;
+      int32_t yOffset = (screenH - drawH) / 2;
+
+      renderer.drawBitmap(bmp, xOffset, yOffset, drawW, drawH);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/util/BmpViewerActivity.cpp` around lines 26 - 33, The code
currently centers the BMP using xOffset/yOffset and bmpW/bmpH but does not scale
images larger than the screen, causing clipping; modify the drawing logic
(around renderer.drawBitmap) to compute a scale = min(1.0,
renderer.getScreenWidth() / float(bmpW), renderer.getScreenHeight() /
float(bmpH)) to preserve aspect ratio, then set drawW = int(bmpW * scale), drawH
= int(bmpH * scale), recalc xOffset/yOffset using drawW/drawH to center, and
call renderer.drawBitmap(bmp, xOffset, yOffset, drawW, drawH) so large images
are downscaled to fit while small images remain unscaled.

43-44: Consider localizing the "« Back" button label.

Other activities use the tr() function for button labels (e.g., tr(STR_BACK) in MyLibraryActivity.cpp). For consistency and internationalization support, this hardcoded string should use the localization system.

💬 Proposed fix

Add the I18n include and use the localized string:

+#include <I18n.h>
-  const auto labels = mappedInput.mapLabels("« Back", "", "", "");
+  const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/util/BmpViewerActivity.cpp` around lines 43 - 44, The
hardcoded "« Back" label passed to mappedInput.mapLabels should be replaced with
the localized string using tr(STR_BACK); update the include list to pull in the
I18n header (so tr/STR_BACK are available), then call
mappedInput.mapLabels(tr(STR_BACK), "", "", ""); keep the rest
(GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3,
labels.btn4)) unchanged to preserve behavior.
src/activities/reader/ReaderActivity.cpp (1)

94-97: currentBookPath is not updated for BMP files.

Other reader navigation methods (e.g., onGoToEpubReader, onGoToXtcReader, onGoToTxtReader) update currentBookPath before transitioning. This is skipped for BMP files. While BMP is not a "book," consider if consistency is desired for any tracking or navigation purposes.

💡 Optional: Set currentBookPath for consistency
 void ReaderActivity::onGoToBmpViewer(const std::string& path) {
+  currentBookPath = path;
   exitActivity();
   enterNewActivity(new BmpViewerActivity(renderer, mappedInput, path, [this, path] { goToLibrary(path); }));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/reader/ReaderActivity.cpp` around lines 94 - 97, The
onGoToBmpViewer path transition doesn't set currentBookPath like the other
readers; update currentBookPath (e.g., assign currentBookPath = path) before
calling exitActivity()/enterNewActivity so the BmpViewerActivity transition
behaves consistently with onGoToEpubReader/onGoToXtcReader/onGoToTxtReader and
any navigation/tracking logic that relies on currentBookPath (affecting symbols:
onGoToBmpViewer, currentBookPath, exitActivity, enterNewActivity,
BmpViewerActivity, goToLibrary).
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97c3314 and f9dc0ad.

📒 Files selected for processing (6)
  • src/activities/home/MyLibraryActivity.cpp
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/ReaderActivity.h
  • src/activities/util/BmpViewerActivity.cpp
  • src/activities/util/BmpViewerActivity.h
  • src/components/themes/lyra/LyraTheme.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/util/BmpViewerActivity.h
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/util/BmpViewerActivity.cpp
  • src/activities/home/MyLibraryActivity.cpp
  • src/activities/reader/ReaderActivity.h
🧬 Code graph analysis (4)
src/components/themes/lyra/LyraTheme.cpp (1)
src/components/themes/BaseTheme.h (1)
  • x (11-121)
src/activities/util/BmpViewerActivity.h (2)
src/activities/util/BmpViewerActivity.cpp (3)
  • BmpViewerActivity (10-12)
  • onEnter (14-46)
  • onEnter (14-14)
src/activities/reader/ReaderActivity.cpp (2)
  • onEnter (115-148)
  • onEnter (115-115)
src/activities/reader/ReaderActivity.cpp (2)
src/util/StringUtils.cpp (4)
  • checkFileExtension (38-50)
  • checkFileExtension (38-38)
  • checkFileExtension (52-62)
  • checkFileExtension (52-52)
src/main.cpp (5)
  • exitActivity (133-139)
  • exitActivity (133-133)
  • enterNewActivity (141-144)
  • enterNewActivity (141-141)
  • renderer (37-37)
src/activities/reader/ReaderActivity.h (2)
src/main.cpp (3)
  • onGoHome (212-212)
  • onGoHome (251-255)
  • onGoHome (251-251)
src/activities/reader/ReaderActivity.cpp (8)
  • isBmpFile (33-33)
  • isBmpFile (33-33)
  • extractFolderPath (16-22)
  • extractFolderPath (16-16)
  • goToLibrary (80-84)
  • goToLibrary (80-80)
  • onGoToBmpViewer (94-97)
  • onGoToBmpViewer (94-94)
⏰ 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). (2)
  • GitHub Check: build
  • GitHub Check: cppcheck
🔇 Additional comments (4)
src/activities/home/MyLibraryActivity.cpp (1)

93-96: LGTM!

The .bmp extension is correctly added following the existing pattern. The StringUtils::checkFileExtension function handles case-insensitive matching, so this will properly detect BMP files regardless of case.

src/activities/util/BmpViewerActivity.h (1)

1-21: LGTM!

The header is well-structured and follows the existing activity patterns in the codebase. The class correctly inherits from Activity, declares the necessary lifecycle overrides, and uses appropriate member types for the file path and callback.

src/activities/reader/ReaderActivity.cpp (2)

33-34: LGTM!

The isBmpFile implementation is consistent with the existing isXtcFile and isTxtFile helpers.


124-126: LGTM!

BMP file detection is correctly integrated into the onEnter flow, routing to the BMP viewer before other format checks.

🤖 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/ReaderActivity.h`:
- Line 15: The onGoHome std::function member in ReaderActivity.h is declared but
not initialized; update the ReaderActivity constructor to accept a
std::function<void()> onGoHome parameter (matching
XtcReaderActivity/TxtReaderActivity/EpubReaderActivity patterns) and initialize
the onGoHome member in the constructor initializer list so the member is not
left uninitialized.

In `@src/components/themes/lyra/LyraTheme.cpp`:
- Around line 247-257: Replace the unqualified color identifier used in the
button rendering calls with the namespaced enum to match the rest of the file:
update the two occurrences where fillRoundedRect is called with White (and the
corresponding drawRoundedRect nearby) to use Color::White instead; you can find
them in the LyraTheme.cpp button-drawing block where renderer.fillRoundedRect /
renderer.drawRoundedRect are invoked for the regular and SMALL-sized button
(references: SMALL_FONT_ID, labels[i], smallButtonHeight).

---

Nitpick comments:
In `@src/activities/reader/ReaderActivity.cpp`:
- Around line 94-97: The onGoToBmpViewer path transition doesn't set
currentBookPath like the other readers; update currentBookPath (e.g., assign
currentBookPath = path) before calling exitActivity()/enterNewActivity so the
BmpViewerActivity transition behaves consistently with
onGoToEpubReader/onGoToXtcReader/onGoToTxtReader and any navigation/tracking
logic that relies on currentBookPath (affecting symbols: onGoToBmpViewer,
currentBookPath, exitActivity, enterNewActivity, BmpViewerActivity,
goToLibrary).

In `@src/activities/util/BmpViewerActivity.cpp`:
- Around line 26-33: The code currently centers the BMP using xOffset/yOffset
and bmpW/bmpH but does not scale images larger than the screen, causing
clipping; modify the drawing logic (around renderer.drawBitmap) to compute a
scale = min(1.0, renderer.getScreenWidth() / float(bmpW),
renderer.getScreenHeight() / float(bmpH)) to preserve aspect ratio, then set
drawW = int(bmpW * scale), drawH = int(bmpH * scale), recalc xOffset/yOffset
using drawW/drawH to center, and call renderer.drawBitmap(bmp, xOffset, yOffset,
drawW, drawH) so large images are downscaled to fit while small images remain
unscaled.
- Around line 43-44: The hardcoded "« Back" label passed to
mappedInput.mapLabels should be replaced with the localized string using
tr(STR_BACK); update the include list to pull in the I18n header (so tr/STR_BACK
are available), then call mappedInput.mapLabels(tr(STR_BACK), "", "", ""); keep
the rest (GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3,
labels.btn4)) unchanged to preserve behavior.

removed unused function declaration and changing White to Color::White
clang clang clang
CaptainFrito
CaptainFrito previously approved these changes Feb 17, 2026
Copy link
Contributor

@CaptainFrito CaptainFrito left a comment

Choose a reason for hiding this comment

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

Solid

@Levrk
Copy link
Contributor Author

Levrk commented Feb 17, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Levrk
Copy link
Contributor Author

Levrk commented Feb 17, 2026

@osteotek Any chance you could take a look at this? I saw you just approved #948 which would completely negate this change as it directly involves viewing .bmp files in the file browser.

@osteotek
Copy link
Member

@Levrk I am not sure if #948 negates this as images can still be kept in sleep directory?

@Levrk
Copy link
Contributor Author

Levrk commented Feb 17, 2026

@osteotek You know what fair enough I did not realize the backwards compatibility with a visible sleep folder.

@Levrk Levrk mentioned this pull request Feb 18, 2026
@allgoewer
Copy link
Contributor

allgoewer commented Feb 19, 2026

Maybe this is more of a feature for another PR after this gets merged, but:

It would be great if one could use another button for a one-off "display and sleep". That would fit nicely with the display-without-power-consumption capabilities of the device.

@Levrk
Copy link
Contributor Author

Levrk commented Feb 19, 2026

Maybe this is more of a feature for another PR after this gets merged, but:

It would be great if one could use another button for a one-off "display and sleep". That would fit nicely with the display-without-power-consumption capabilities of the device.

Totally agree, I like the idea of being able to have a big collection of random sleep images but still being able to select one in particular if you so choose. Hoping to get this merged with the most basic functionality and then work on improving it later just to make sure I stay in scope.

@ariel-lindemann
Copy link
Contributor

Does it take into account the settings for Sleep Screen Cover Mode and Sleep Screen Cover Filter?

I think a big use case of this feature will be to see what the image would look like as the cover, so it would be good to take that into account.

@el
Copy link
Contributor

el commented Feb 20, 2026

This would be very useful with #759

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for opening and viewing .bmp files directly from the file browser by introducing a dedicated BmpViewerActivity, and updates the Lyra theme’s button-hint rendering to avoid drawing artifacts.

Changes:

  • Added BmpViewerActivity to load and display BMP images with a simple “Back” exit path.
  • Updated ReaderActivity and MyLibraryActivity to recognize .bmp files and route them to the viewer.
  • Switched Lyra theme button hint background fills to rounded-rect fills (aligning with the existing rounded border rendering).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/components/themes/lyra/LyraTheme.cpp Uses fillRoundedRect for button hint backgrounds to match rounded borders and prevent visual artifacts.
src/activities/util/BmpViewerActivity.h Declares new activity for BMP viewing.
src/activities/util/BmpViewerActivity.cpp Implements BMP loading/rendering and back navigation UI.
src/activities/reader/ReaderActivity.h Adds BMP detection and navigation hook to launch BMP viewer.
src/activities/reader/ReaderActivity.cpp Routes .bmp files to BmpViewerActivity when opened.
src/activities/home/MyLibraryActivity.cpp Includes .bmp in the file-browser extension filter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@osteotek
Copy link
Member

osteotek commented Feb 20, 2026

@Levrk Looks good to me, just a few suggestions for small fixes, as suggested by Copilot.

@osteotek
Copy link
Member

osteotek commented Feb 20, 2026

This would be very useful with #759

Agreed! But code from this PR cannot open screenshots made by #759 branch. Just shows black screen (no log messages), other bmps are opening fine. Not sure which PR should be fixed
Edit: seems to be fixed, at least some screenshots are opening

osteotek
osteotek previously approved these changes Feb 20, 2026
@osteotek osteotek requested a review from a team February 20, 2026 15:18
@Levrk
Copy link
Contributor Author

Levrk commented Feb 20, 2026

fixed scaling/cropping to work for larger files. Larger files made me add a loading indicator so it doesn't feel like it is just hanging when you opening something big

Verified this works with a bunch of different .bmp files

@osteotek osteotek merged commit 7717ae2 into crosspoint-reader:master Feb 21, 2026
6 checks passed
lukestein pushed a commit to lukestein/crosspoint-reader that referenced this pull request Feb 21, 2026
crosspoint-reader#887)

## Summary

* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)

Implements new feature for viewing .bmp files directly from the "Browse
Files" menu.

* **What changes are included?**

You can now view .bmp files when browsing. You can click the select
button to open the file, and then click back to close it and continue
browsing in the same location. Once open a file will display on the
screen with no additional options to interact outside of exiting with
the back button.

The attached video shows this feature in action:


https://github.com/user-attachments/assets/9659b6da-abf7-4458-b158-e11c248c8bef

## Additional Context

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

The changes implemented in crosspoint-reader#884 are also present here as this feature is
actually what led to me noticing this issue. I figured I would add that
PR as a separate request in case that one could be more easily merged
given this feature is significantly more complicated and will likely be
subject to more intense review.

---

### 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 <[email protected]>
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.

8 participants