CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
ImageView.cpp
Go to the documentation of this file.
1
7
9
10#include "cdc_views/KeyCodes.h"
13#include "cdc_ui/I18n.h"
14#include "cdc_ui/ViewStack.h"
15#include "cdc_hal/IDisplay.h"
16#include "cdc_log.h"
17
18#include "cdc_image/Dither.h"
19#include "cdc_image/ImageDecoder.h"
20
21#include <goodisplay/gdey029T94.h>
22
23#include <cstring>
24
25static const char* TAG = "ImageView";
26
28
29namespace cdc::ui {
30
31void ImageView::init(const char* title, const uint8_t* data, size_t len) {
32 if (title) {
33 strncpy(titleBuf_, title, MAX_TITLE_LEN - 1);
34 titleBuf_[MAX_TITLE_LEN - 1] = '\0';
35 } else {
36 titleBuf_[0] = '\0';
37 }
38
39 errorKey_ = nullptr;
40 fitBits_.reset();
41 fullBits_.reset();
42 fitBmp_ = {};
43 fullBmp_ = {};
44 mode_ = Mode::Fit;
45 panX_ = 0;
46 panY_ = 0;
47 dirty_ = true;
48
49 uint16_t w = 0, h = 0;
50 const char* err = nullptr;
51 auto gray = cdc::image::decodeToGray(data, len, MAX_PIXELS, w, h, err);
52 if (!gray) {
53 errorKey_ = err ? err : "core.img_decode_failed";
54 return;
55 }
56
57 auto scratch = cdc::core::psramAlloc<int16_t>(cdc::image::Ditherer::scratchCells(w));
58 if (!scratch) {
59 errorKey_ = "core.img_decode_failed";
60 return;
61 }
62
63 // Native-resolution bitmap (actual-size / pan).
64 fullBits_ = cdc::core::psramAlloc<uint8_t>(cdc::image::MonoBitmap::byteSize(w, h));
65 if (fullBits_) {
66 fullBmp_.width = w;
67 fullBmp_.height = h;
68 fullBmp_.stride = cdc::image::MonoBitmap::strideFor(w);
69 fullBmp_.bits = fullBits_.get();
70 cdc::image::ditherImage(gray.get(), w, h, fullBmp_.bits, scratch.get());
71 }
72
73 // Fit-to-screen bitmap.
75 const uint16_t scrW = display ? display->getWidth() : 296;
76 const uint16_t scrH = display ? display->getHeight() : 128;
77 const uint16_t areaH = static_cast<uint16_t>(scrH - FOOTER_HEIGHT);
78
79 uint16_t fw = w, fh = h;
80 if (w > scrW || h > areaH) {
81 const uint32_t sx = (static_cast<uint32_t>(scrW) * 1000) / w;
82 const uint32_t sy = (static_cast<uint32_t>(areaH) * 1000) / h;
83 const uint32_t s = sx < sy ? sx : sy;
84 fw = static_cast<uint16_t>((static_cast<uint32_t>(w) * s) / 1000);
85 fh = static_cast<uint16_t>((static_cast<uint32_t>(h) * s) / 1000);
86 if (fw == 0) fw = 1;
87 if (fh == 0) fh = 1;
88 }
89
90 fitBits_ = cdc::core::psramAlloc<uint8_t>(cdc::image::MonoBitmap::byteSize(fw, fh));
91 auto fitGray = cdc::core::psramAlloc<uint8_t>(static_cast<size_t>(fw) * fh);
92 if (fitBits_ && fitGray) {
93 fitBmp_.width = fw;
94 fitBmp_.height = fh;
95 fitBmp_.stride = cdc::image::MonoBitmap::strideFor(fw);
96 fitBmp_.bits = fitBits_.get();
97 cdc::image::downscaleGrayBox(gray.get(), w, h, fitGray.get(), fw, fh);
98 cdc::image::ditherImage(fitGray.get(), fw, fh, fitBmp_.bits, scratch.get());
99 }
100
101 LOG_D(TAG, "init: %ux%u -> fit %ux%u", w, h, fw, fh);
102 // gray, fitGray, scratch released on scope exit.
103}
104
106 fitBits_.reset();
107 fullBits_.reset();
108 fitBmp_ = {};
109 fullBmp_ = {};
110 errorKey_ = nullptr;
111}
112
113void ImageView::clampPan() {
115 const int scrW = display ? display->getWidth() : 296;
116 const int scrH = display ? display->getHeight() : 128;
117 const int areaH = scrH - FOOTER_HEIGHT;
118 const int maxX = fullBmp_.width > scrW ? fullBmp_.width - scrW : 0;
119 const int maxY = fullBmp_.height > areaH ? fullBmp_.height - areaH : 0;
120 if (panX_ < 0) panX_ = 0;
121 if (panY_ < 0) panY_ = 0;
122 if (panX_ > maxX) panX_ = maxX;
123 if (panY_ > maxY) panY_ = maxY;
124}
125
126const char* ImageView::getFooterHint() const {
127 return ui::tr("core.hint_image");
128}
129
131 if (errorKey_) {
133 }
134 switch (key) {
135 case KEY_NO:
137 case KEY_SELECT:
138 mode_ = (mode_ == Mode::Fit) ? Mode::Actual : Mode::Fit;
139 panX_ = 0;
140 panY_ = 0;
141 dirty_ = true;
143 default:
144 break;
145 }
146 if (mode_ == Mode::Actual) {
147 switch (key) {
148 case KEY_UP: panY_ -= PAN_STEP; clampPan(); dirty_ = true; return InputResult::CONSUMED;
149 case KEY_DOWN: panY_ += PAN_STEP; clampPan(); dirty_ = true; return InputResult::CONSUMED;
150 case '4': panX_ -= PAN_STEP; clampPan(); dirty_ = true; return InputResult::CONSUMED;
151 case '6': panX_ += PAN_STEP; clampPan(); dirty_ = true; return InputResult::CONSUMED;
152 default: break;
153 }
154 }
156}
157
158void ImageView::render(bool partial) {
160 if (!display) return;
161 auto* gfx = static_cast<Gdey029T94*>(display->getNativeHandle());
162 if (!gfx) return;
163
164 const uint16_t width = display->getWidth();
165 const uint16_t height = display->getHeight();
166 const int areaBottom = height - FOOTER_HEIGHT;
167
168 gfx->fillScreen(EPD_WHITE);
169 gfx->setFont(nullptr);
170 gfx->setTextColor(EPD_BLACK);
171 gfx->setTextSize(1);
172
173 if (errorKey_) {
174 gfx->setCursor(8, areaBottom / 2);
175 render::printText(gfx, ui::tr(errorKey_));
176 render::drawFooterBar(gfx, width, height, titleBuf_[0] ? titleBuf_ : nullptr,
177 getFooterHint(), true);
178 dirty_ = false;
179 return;
180 }
181
182 if (mode_ == Mode::Fit && fitBmp_.bits) {
183 int x = (static_cast<int>(width) - fitBmp_.width) / 2;
184 int y = (areaBottom - fitBmp_.height) / 2;
185 if (x < 0) x = 0;
186 if (y < 0) y = 0;
187 gfx->drawBitmap(x, y, fitBmp_.bits, fitBmp_.width, fitBmp_.height, EPD_BLACK);
188 } else if (fullBmp_.bits) {
189 gfx->drawBitmap(-panX_, -panY_, fullBmp_.bits, fullBmp_.width, fullBmp_.height, EPD_BLACK);
190 }
191
192 render::drawFooterBar(gfx, width, height, titleBuf_[0] ? titleBuf_ : nullptr,
193 getFooterHint(), true);
194 dirty_ = false;
195}
196
198
199ImageView* showImage(const char* title, const uint8_t* data, size_t len) {
200 s_sharedImageView.init(title, data, len);
202 return &s_sharedImageView;
203}
204
205} // namespace cdc::ui
static const char * TAG
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
constexpr int FOOTER_HEIGHT
Footer bar height in pixels (used by drawFooterBar).
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_D(tag, fmt,...)
Definition cdc_log.h:148
Full-screen viewer for a decoded PNG/JPEG, dithered to the e-paper.
Definition ImageView.h:21
static constexpr uint32_t MAX_PIXELS
~1 megapixel cap.
Definition ImageView.h:23
void init(const char *title, const uint8_t *data, size_t len)
Decodes and lays out an image.
Definition ImageView.cpp:31
const char * getFooterHint() const override
void onExit() override
void render(bool partial) override
InputResult onKey(char key) override
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:53
void push(IView *view, void *context=nullptr)
PsramUniquePtr< T > psramAlloc(std::size_t count) noexcept
Allocate count elements of T in PSRAM (8-bit capable region).
Definition Raii.h:51
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
constexpr int FOOTER_HEIGHT
Footer bar height in pixels (used by drawFooterBar).
void drawFooterBar(Gdey029T94 *gfx, uint16_t width, uint16_t height, const char *prefix, const char *hint, bool force=false)
Draws footer bar with optional prefix and hint text.
void printText(Adafruit_GFX *gfx, const char *text)
Draws CP437 text with the built-in 6x8 glyph font, byte-for-byte.
Centralized key-code constants for cdc_views.
Definition IModule.h:8
const char * tr(const char *key)
Look up a translation by string key.
Definition I18n.h:209
static ImageView s_sharedImageView
static constexpr char KEY_SELECT
Center select / digit '5'.
Definition KeyCodes.h:38
static constexpr char KEY_DOWN
Move selection down (numeric '8').
Definition KeyCodes.h:35
Gdey029T94 * display
InputResult
Definition IView.h:11
static constexpr char KEY_NO
Cancel / Back / Backspace.
Definition KeyCodes.h:44
ImageView * showImage(const char *title, const uint8_t *data, size_t len)
Decodes and shows an image on the view stack.
static const char * TAG
static constexpr char KEY_UP
Move selection up (numeric '2').
Definition KeyCodes.h:32