Public Access
Storage: view pictures, PNG, JPEG, BMP and GIF (#45)
Enter on a picture shows it: shrunk to fit the screen, or at its own size with Enter again and the arrows to move. Dithered to the screen's 256 colours; a colour the screen has exactly is left alone, so screenshots are shown as they are. The picture is decoded once, straight into the screen's buffer, and kept there (App::retainsContent): no copy in memory. Decoding runs on the storage task, so the keys keep working and a 12 megapixel photograph appears as it comes instead of tripping the watchdog. PNG, BMP and GIF are read by decoders of our own, host-tested against files made by Pillow; the PNG one needs 32 KB where the display library's needed 44 KB in one block, which the device often doesn't have. JPEG uses the library's TJpgDec. Also corrects two sentences that still gave 16 KB as the editing limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
@@ -162,11 +162,15 @@ void FileViewer::open(const std::string& path, uint32_t size) {
|
||||
file_ = std::make_shared<fs::File>();
|
||||
std::string name = files::baseName(path);
|
||||
files::FileKind kind = files::kindOf(name);
|
||||
if (kind == files::FileKind::Unknown) { // what do its first bytes look like?
|
||||
files::ImageKind picture = files::imageKindOfName(name);
|
||||
if (kind == files::FileKind::Unknown && picture == files::ImageKind::None) { // what do its first bytes look like?
|
||||
uint8_t head[256];
|
||||
size_t n = readAt(0, head, sizeof head);
|
||||
picture = files::imageKindOfBytes(head, n);
|
||||
kind = files::looksLikeText(head, n) ? files::FileKind::Text : files::FileKind::Unknown;
|
||||
}
|
||||
std::string notShown;
|
||||
if (picture != files::ImageKind::None) notShown = image_.open(path, size);
|
||||
switch (kind) {
|
||||
case files::FileKind::Text: base_ = Mode::Text; break;
|
||||
case files::FileKind::Gpx: base_ = Mode::Gpx; break;
|
||||
@@ -174,11 +178,13 @@ void FileViewer::open(const std::string& path, uint32_t size) {
|
||||
case files::FileKind::Ota: base_ = Mode::Ota; break;
|
||||
default: base_ = Mode::Hex; break;
|
||||
}
|
||||
if (picture != files::ImageKind::None && notShown.empty()) base_ = Mode::Image;
|
||||
pager_.reset(new files::TextPager([this](uint32_t offset, uint8_t* into, size_t len) { return readAt(offset, into, len); }, size_,
|
||||
kCols, kRows));
|
||||
if (files::opensAtEnd(name)) pager_->toEnd();
|
||||
hexTop_ = 0;
|
||||
show(base_);
|
||||
if (!notShown.empty()) say(notShown); // a picture that can't be shown: its bytes, and why
|
||||
if (base_ == Mode::Gpx || base_ == Mode::Pcap || base_ == Mode::Ota) startScan(base_);
|
||||
}
|
||||
|
||||
@@ -203,6 +209,7 @@ void FileViewer::close() {
|
||||
});
|
||||
}
|
||||
file_.reset();
|
||||
image_.close();
|
||||
pager_.reset();
|
||||
confirm_.reset();
|
||||
message_.clear();
|
||||
@@ -214,11 +221,13 @@ void FileViewer::close() {
|
||||
|
||||
void FileViewer::show(Mode mode) {
|
||||
mode_ = mode;
|
||||
if (mode == Mode::Image) image_.lost(); // another view was drawn where it was
|
||||
stale_ = true;
|
||||
rowsFrom_ = -1;
|
||||
}
|
||||
|
||||
void FileViewer::say(const std::string& text) {
|
||||
if (mode_ == Mode::Image) return image_.say(text);
|
||||
message_ = text;
|
||||
messageMs_ = millis();
|
||||
}
|
||||
@@ -275,6 +284,7 @@ void FileViewer::help(std::vector<KeyHelp>& out) const {
|
||||
case Mode::Packet: keys::add(out, keys::kViewerPacket); break;
|
||||
case Mode::Gpx: keys::add(out, keys::kViewerGpx); break;
|
||||
case Mode::Ota: keys::add(out, keys::kViewerOta); break;
|
||||
case Mode::Image: image_.help(out); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,6 +309,7 @@ bool FileViewer::onKey(const KeyEvent& e) {
|
||||
else show(base_ == Mode::Hex || base_ == Mode::Gpx ? Mode::Text : Mode::Hex);
|
||||
return true;
|
||||
}
|
||||
if (mode_ == Mode::Image) return image_.onKey(e), true;
|
||||
uint32_t ch = e.key == Key::Char ? e.ch : 0;
|
||||
switch (mode_) {
|
||||
case Mode::Text:
|
||||
@@ -332,7 +343,8 @@ bool FileViewer::onKey(const KeyEvent& e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileViewer::update(uint32_t) {
|
||||
bool FileViewer::update(uint32_t nowMs) {
|
||||
if (mode_ == Mode::Image) return image_.update(nowMs);
|
||||
if (!message_.empty() && millis() - messageMs_ >= 4000) {
|
||||
message_.clear();
|
||||
return true;
|
||||
@@ -389,6 +401,7 @@ void FileViewer::refresh() {
|
||||
}
|
||||
|
||||
void FileViewer::draw(Canvas& c) {
|
||||
if (mode_ == Mode::Image) return image_.draw(c);
|
||||
const auto& area = theme::kContent;
|
||||
refresh();
|
||||
c.setTextDatum(top_left);
|
||||
|
||||
Reference in New Issue
Block a user