change QR scan box to use the largest centered square of the camera image (minus a small margin)
What changed, and why it matters
This commit changes how the QR code scanning box is sized on the Blockstream Jade hardware wallet. Previously, the scan area was based on how much of the camera image was shown on the device's screen plus a small extra margin. Now it uses the largest centered square that fits in the full camera image, minus a small margin. This is a UI/behavior adjustment, not a security fix.
No security action required. Treat as a normal functional/UI improvement to QR scanning behavior.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch removes camera_displayed_image_width() and camera_displayed_image_height() helpers and replaces the scan box calculation in qrscan.c. The old logic computed an ‘ideal’ scan box from the displayed image dimensions plus SCAN_EXTRA_MARGIN, then clamped to the camera sensor resolution. The new logic directly uses min(CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT) - SCAN_MARGIN for both width and height, making the scan region a centered square based on the full camera frame. Logging levels for scan dimensions were also changed from INFO to ERROR, which appears cosmetic/misclassified rather than security-relevant.
Changed components
main/camera.cmain/camera.hmain/qrscan.cInspect captured patch +11 / −25
diff --git a/main/camera.c b/main/camera.c
index ce5fac0..9aab331 100644
--- a/main/camera.c
+++ b/main/camera.c
@@ -174,10 +174,6 @@ static void copy_camera_image_180(
void await_qr_help_activity(const char* url);
-uint16_t camera_displayed_image_width(void) { return UI2CAM(DISPLAY_IMAGE_WIDTH); }
-
-uint16_t camera_displayed_image_height(void) { return UI2CAM(DISPLAY_IMAGE_HEIGHT); }
-
gui_activity_t* make_camera_activity(gui_view_node_t** image_node, gui_view_node_t** label_node, bool show_click_btn,
qr_frame_guides_t qr_frame_guides, progress_bar_t* progress_bar, bool show_help_btn);
diff --git a/main/camera.h b/main/camera.h
index 8c8bfe2..a369cf2 100644
--- a/main/camera.h
+++ b/main/camera.h
@@ -13,10 +13,6 @@
#define CAMERA_IMAGE_WIDTH 320
#define CAMERA_IMAGE_HEIGHT 240
-// How much image (central area) is displayed on screen
-uint16_t camera_displayed_image_width(void);
-uint16_t camera_displayed_image_height(void);
-
// Function to process images from the camera.
// Should return false if processing incomplete (and so should be called again with the next frame)
// Should return true when processing complete (and the image capture loop/task should exit)
diff --git a/main/qrscan.c b/main/qrscan.c
index 7a03b4e..c87fc63 100644
--- a/main/qrscan.c
+++ b/main/qrscan.c
@@ -10,7 +10,7 @@
#include "utils/malloc_ext.h"
#include "utils/util.h"
-#define SCAN_EXTRA_MARGIN 20
+#define SCAN_MARGIN 20
// Inspect qrcodes and try to extract payload - whether any were seen and any
// string data extracted are stored in the qr_data struct passed.
@@ -131,17 +131,14 @@ bool scan_qr(const size_t width, const size_t height, const uint8_t* data, const
qr_data->ds = JADE_MALLOC_DRAM(sizeof(struct datastream));
JADE_ASSERT(qr_data->ds);
- // Also correctly size the internal image buffer since we know the size of the camera images/display.
- const uint16_t ideal_scan_box_size
- = min_u16(camera_displayed_image_width(), camera_displayed_image_height()) + SCAN_EXTRA_MARGIN;
- const uint16_t scan_width = min_u16(ideal_scan_box_size, CAMERA_IMAGE_WIDTH);
- const uint16_t scan_height = min_u16(ideal_scan_box_size, CAMERA_IMAGE_HEIGHT);
- const int qret = quirc_resize(qr_data->q, scan_width, scan_height);
+ // Also correctly size the internal image buffer since we know the size of the camera images.
+ const uint16_t scan_width = min_u16(CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT) - SCAN_MARGIN;
+ const int qret = quirc_resize(qr_data->q, scan_width, scan_width);
JADE_ASSERT(qret == 0);
qr_data->len = 0;
- JADE_LOGI("SCAN WIDTH: %u", scan_width);
- JADE_LOGI("SCAN HEIGHT: %u", scan_height);
+ JADE_LOGE("SCAN WIDTH: %u", scan_width);
+ JADE_LOGE("SCAN HEIGHT: %u", scan_width);
const bool ret = qr_recognize(width, height, data, len, qr_data);
@@ -174,18 +171,15 @@ bool jade_camera_scan_qr(
qr_data->ds = JADE_MALLOC_DRAM(sizeof(struct datastream));
JADE_ASSERT(qr_data->ds);
- // Also correctly size the internal image buffer since we know the size of the camera images/display.
+ // Also correctly size the internal image buffer since we know the size of the camera images.
// This image buffer is then reused for every camera image frame processed.
- const uint16_t ideal_scan_box_size
- = min_u16(camera_displayed_image_width(), camera_displayed_image_height()) + SCAN_EXTRA_MARGIN;
- const uint16_t scan_width = min_u16(ideal_scan_box_size, CAMERA_IMAGE_WIDTH);
- const uint16_t scan_height = min_u16(ideal_scan_box_size, CAMERA_IMAGE_HEIGHT);
- const int qret = quirc_resize(qr_data->q, scan_width, scan_height);
+ const uint16_t scan_width = min_u16(CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT) - SCAN_MARGIN;
+ const int qret = quirc_resize(qr_data->q, scan_width, scan_width);
JADE_ASSERT(qret == 0);
qr_data->len = 0;
- JADE_LOGI("SCAN WIDTH: %u", scan_width);
- JADE_LOGI("SCAN HEIGHT: %u", scan_height);
+ JADE_LOGE("SCAN WIDTH: %u", scan_width);
+ JADE_LOGE("SCAN HEIGHT: %u", scan_width);
// Run the camera task trying to interpet frames as qr-codes
const bool show_camera_ui = true;
Why this scored 13/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.