feat: Added BmpViewer activity for viewing .bmp images in file browser (#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 #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 <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
101
src/activities/util/BmpViewerActivity.cpp
Normal file
101
src/activities/util/BmpViewerActivity.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "BmpViewerActivity.h"
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
BmpViewerActivity::BmpViewerActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string path,
|
||||
std::function<void()> onGoBack)
|
||||
: Activity("BmpViewer", renderer, mappedInput), filePath(std::move(path)), onGoBack(std::move(onGoBack)) {}
|
||||
|
||||
void BmpViewerActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
// Removed the redundant initial renderer.clearScreen()
|
||||
|
||||
FsFile file;
|
||||
|
||||
const auto pageWidth = renderer.getScreenWidth();
|
||||
const auto pageHeight = renderer.getScreenHeight();
|
||||
Rect popupRect = GUI.drawPopup(renderer, tr(STR_LOADING_POPUP));
|
||||
GUI.fillPopupProgress(renderer, popupRect, 20); // Initial 20% progress
|
||||
// 1. Open the file
|
||||
if (Storage.openFileForRead("BMP", filePath, file)) {
|
||||
Bitmap bitmap(file, true);
|
||||
|
||||
// 2. Parse headers to get dimensions
|
||||
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
||||
int x, y;
|
||||
|
||||
if (bitmap.getWidth() > pageWidth || bitmap.getHeight() > pageHeight) {
|
||||
float ratio = static_cast<float>(bitmap.getWidth()) / static_cast<float>(bitmap.getHeight());
|
||||
const float screenRatio = static_cast<float>(pageWidth) / static_cast<float>(pageHeight);
|
||||
|
||||
if (ratio > screenRatio) {
|
||||
// Wider than screen
|
||||
x = 0;
|
||||
y = std::round((static_cast<float>(pageHeight) - static_cast<float>(pageWidth) / ratio) / 2);
|
||||
} else {
|
||||
// Taller than screen
|
||||
x = std::round((static_cast<float>(pageWidth) - static_cast<float>(pageHeight) * ratio) / 2);
|
||||
y = 0;
|
||||
}
|
||||
} else {
|
||||
// Center small images
|
||||
x = (pageWidth - bitmap.getWidth()) / 2;
|
||||
y = (pageHeight - bitmap.getHeight()) / 2;
|
||||
}
|
||||
|
||||
// 4. Prepare Rendering
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
||||
GUI.fillPopupProgress(renderer, popupRect, 50);
|
||||
|
||||
renderer.clearScreen();
|
||||
// Assuming drawBitmap defaults to 0,0 crop if omitted, or pass explicitly: drawBitmap(bitmap, x, y, pageWidth,
|
||||
// pageHeight, 0, 0)
|
||||
renderer.drawBitmap(bitmap, x, y, pageWidth, pageHeight, 0, 0);
|
||||
|
||||
// Draw UI hints on the base layer
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
// Single pass for non-grayscale images
|
||||
|
||||
renderer.displayBuffer(HalDisplay::FULL_REFRESH);
|
||||
|
||||
} else {
|
||||
// Handle file parsing error
|
||||
renderer.clearScreen();
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "Invalid BMP File");
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
|
||||
file.close();
|
||||
} else {
|
||||
// Handle file open error
|
||||
renderer.clearScreen();
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "Could not open file");
|
||||
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", "");
|
||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
renderer.displayBuffer(HalDisplay::FULL_REFRESH);
|
||||
}
|
||||
}
|
||||
|
||||
void BmpViewerActivity::onExit() {
|
||||
Activity::onExit();
|
||||
renderer.clearScreen();
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
}
|
||||
|
||||
void BmpViewerActivity::loop() {
|
||||
// Keep CPU awake/polling so 1st click works
|
||||
Activity::loop();
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
if (onGoBack) onGoBack();
|
||||
return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user