remove qr guide icons and draw qr guide directly
What changed, and why it matters
This commit is a straightforward user-interface cleanup: it removes pre-made QR guide corner images and instead draws the same corner guides directly using simple rectangles. There is no security change visible in the code.
No security action required. Treat as a normal UI refactor and verify visually that the new procedural guides align correctly on both ESP32 (QVGA) and ESP32-S3 (VGA) screens.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch deletes four compressed binary icon assets and the CMake references that embedded them. It adds a new QRGUIDE GUI node type with a procedural renderer (render_qrguide) that draws L-shaped corner marks via display_fill_rect. The qr_frame_guides_t enum is simplified from {NONE, SMALL, LARGE} to {NONE, SHOW}, and all call sites now use QR_GUIDES_SHOW. The camera activity now creates a qrguide node instead of loading an icon asset. No cryptographic, input-validation, memory-safety, or privilege changes are present.
Changed components
main/gui.cmain/gui.hmain/ui/camera.cmain/ui.hmain/bcur.cmain/process/debug_scan_qr.cmain/process/mnemonic.cmain/process/register_otp.clogo/icon_qrguide_*.bin.gzmain/CMakeLists.txtInspect captured patch +80 / −47
diff --git a/logo/icon_qrguide_qvga_large.bin.gz b/logo/icon_qrguide_qvga_large.bin.gz
deleted file mode 100644
index b106138..0000000
Binary files a/logo/icon_qrguide_qvga_large.bin.gz and /dev/null differ
diff --git a/logo/icon_qrguide_qvga_small.bin.gz b/logo/icon_qrguide_qvga_small.bin.gz
deleted file mode 100644
index e3a7b32..0000000
Binary files a/logo/icon_qrguide_qvga_small.bin.gz and /dev/null differ
diff --git a/logo/icon_qrguide_vga_large.bin.gz b/logo/icon_qrguide_vga_large.bin.gz
deleted file mode 100644
index 7f05bc0..0000000
Binary files a/logo/icon_qrguide_vga_large.bin.gz and /dev/null differ
diff --git a/logo/icon_qrguide_vga_small.bin.gz b/logo/icon_qrguide_vga_small.bin.gz
deleted file mode 100644
index b1c5655..0000000
Binary files a/logo/icon_qrguide_vga_small.bin.gz and /dev/null differ
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 3fdd24f..d491dc4 100755
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -24,11 +24,8 @@ else()
endif()
if (CONFIG_IDF_TARGET_ESP32S3)
- list(APPEND logo_files ${PROJECT_DIR}/logo/icon_qrguide_vga_large.bin.gz ${PROJECT_DIR}/logo/icon_qrguide_vga_small.bin.gz)
set(attestdir "attestation")
set(usbdir "usbhmsc")
-else()
- list(APPEND logo_files ${PROJECT_DIR}/logo/icon_qrguide_qvga_large.bin.gz ${PROJECT_DIR}/logo/icon_qrguide_qvga_small.bin.gz)
endif()
if(CONFIG_AMALGAMATED_BUILD)
diff --git a/main/bcur.c b/main/bcur.c
index 5aed1f6..4eb8d83 100644
--- a/main/bcur.c
+++ b/main/bcur.c
@@ -670,7 +670,7 @@ bool bcur_scan_qr(
qr_data_t qr_data = { .len = 0, .is_valid = collect_any_bcur, .ctx = urdecoder, .progress_bar = &progress_bar };
// Scan qr code using the bcur decoder to collate multiple frames if required
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_LARGE;
+ const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SHOW;
if (!jade_camera_scan_qr(&qr_data, prompt_text, qr_frame_guides, help_url)) {
// User exited without completing scanning
urfree_placement_decoder(urdecoder);
diff --git a/main/gui.c b/main/gui.c
index cdffdb2..9d49ad6 100644
--- a/main/gui.c
+++ b/main/gui.c
@@ -1149,6 +1149,17 @@ void gui_make_icon(gui_view_node_t** ptr, const Icon* icon, color_t color, const
make_view_node(ptr, ICON, data, free_view_node_icon_data);
}
+void gui_make_qrguide(gui_view_node_t** ptr, color_t color)
+{
+ JADE_INIT_OUT_PPTR(ptr);
+
+ struct view_node_qrguide_data* data = JADE_CALLOC(1, sizeof(struct view_node_qrguide_data));
+
+ data->color = color;
+
+ make_view_node(ptr, QRGUIDE, data, NULL);
+}
+
static bool icon_animation_frame_callback(gui_view_node_t* node, void* extra_args)
{
// no node, invalid node, not yet rendered...
@@ -2041,6 +2052,56 @@ static void render_picture(gui_view_node_t* node, const dispWin_t cs, const uint
}
}
+// render a qrguide to screen
+static void render_qrguide(gui_view_node_t* node, const dispWin_t cs, const uint8_t depth)
+{
+ JADE_ASSERT(node);
+ JADE_ASSERT(node->kind == QRGUIDE);
+
+ // guide dimensions
+ const uint16_t gwidth = 2;
+ const uint16_t glength = 30;
+ const uint16_t gnubbin = 2;
+
+ // maximum square that fits in the constraints
+ const uint16_t width = cs.x2 - cs.x1;
+ const uint16_t height = cs.y2 - cs.y1;
+ const uint16_t square_size = min_u16(width, height);
+ // guides 3% inset
+ const uint16_t inset = square_size / 30;
+ // guide boundaries
+ const uint16_t left = cs.x1 + (width - square_size) / 2 + inset;
+ const uint16_t right = cs.x2 - (width - square_size) / 2 - inset;
+ const uint16_t top = cs.y1 + (height - square_size) / 2 + inset;
+ const uint16_t bottom = cs.y2 - (height - square_size) / 2 - inset;
+ // top-left
+ display_fill_rect(left, top, gwidth, glength, node->qrguide->color);
+ display_fill_rect(left, top, glength, gwidth, node->qrguide->color);
+ display_fill_rect(left + glength, top, gnubbin, gwidth / 2, node->qrguide->color);
+ display_fill_rect(left, top + glength, gwidth / 2, gnubbin, node->qrguide->color);
+ // top-right
+ display_fill_rect(right - gwidth, top, gwidth, glength, node->qrguide->color);
+ display_fill_rect(right - glength, top, glength, gwidth, node->qrguide->color);
+ display_fill_rect(right - glength - gnubbin, top, gnubbin, gwidth / 2, node->qrguide->color);
+ display_fill_rect(right - gwidth / 2, top + glength, gwidth / 2, gnubbin, node->qrguide->color);
+ // bottom-left
+ display_fill_rect(left, bottom - glength, gwidth, glength, node->qrguide->color);
+ display_fill_rect(left, bottom - gwidth, glength, gwidth, node->qrguide->color);
+ display_fill_rect(left + glength, bottom - gwidth / 2, gnubbin, gwidth / 2, node->qrguide->color);
+ display_fill_rect(left, bottom - glength - gnubbin, gwidth / 2, gnubbin, node->qrguide->color);
+ // bottom-right
+ display_fill_rect(right - gwidth, bottom - glength, gwidth, glength, node->qrguide->color);
+ display_fill_rect(right - glength, bottom - gwidth, glength, gwidth, node->qrguide->color);
+ display_fill_rect(right - glength - gnubbin, bottom - gwidth / 2, gnubbin, gwidth / 2, node->qrguide->color);
+ display_fill_rect(right - gwidth / 2, bottom - glength - gnubbin, gwidth / 2, gnubbin, node->qrguide->color);
+
+ // Draw any children directly over the current node
+ gui_view_node_t* ptr = node->child;
+ if (ptr) {
+ render_node(ptr, cs, depth + 1);
+ }
+}
+
// paint the borders for a view_node
static void paint_borders(gui_view_node_t* node, const dispWin_t cs)
{
@@ -2121,6 +2182,9 @@ static void repaint_node(gui_view_node_t* node)
case PICTURE:
render_picture(node, node->render_data.padded_constraints, node->render_data.depth);
break;
+ case QRGUIDE:
+ render_qrguide(node, node->render_data.padded_constraints, node->render_data.depth);
+ break;
}
}
diff --git a/main/gui.h b/main/gui.h
index 8bca0d6..2c03fab 100644
--- a/main/gui.h
+++ b/main/gui.h
@@ -303,8 +303,13 @@ struct view_node_picture_data {
enum gui_vertical_align valign;
};
+// Data for a qrguide node
+struct view_node_qrguide_data {
+ color_t color;
+};
+
// Possible types of a view_node
-enum __attribute__((__packed__)) view_node_kind { HSPLIT, VSPLIT, TEXT, FILL, BUTTON, ICON, PICTURE };
+enum __attribute__((__packed__)) view_node_kind { HSPLIT, VSPLIT, TEXT, FILL, BUTTON, ICON, PICTURE, QRGUIDE };
typedef struct wait_data {
wait_event_data_t* event_data;
@@ -374,6 +379,7 @@ struct __attribute__((__packed__)) gui_view_node_t {
struct view_node_button_data* button;
struct view_node_icon_data* icon;
struct view_node_picture_data* picture;
+ struct view_node_qrguide_data* qrguide;
};
// (optional) destructor
free_callback_t free_callback;
@@ -432,6 +438,7 @@ void gui_make_text(gui_view_node_t** ptr, const char* text, color_t color);
void gui_make_text_font(gui_view_node_t** ptr, const char* text, color_t color, uint32_t font);
void gui_make_icon(gui_view_node_t** ptr, const Icon* icon, color_t color, const color_t* bg_color);
void gui_make_picture(gui_view_node_t** ptr, const Picture* picture);
+void gui_make_qrguide(gui_view_node_t** ptr, color_t color);
void gui_set_margins(gui_view_node_t* node, uint32_t sides, ...);
void gui_set_padding(gui_view_node_t* node, uint32_t sides, ...);
void gui_set_borders(gui_view_node_t* node, color_t color, uint16_t thickness, uint8_t borders);
diff --git a/main/process/debug_scan_qr.c b/main/process/debug_scan_qr.c
index 6af52d9..aac62a4 100644
--- a/main/process/debug_scan_qr.c
+++ b/main/process/debug_scan_qr.c
@@ -123,7 +123,7 @@ void debug_capture_image_data_process(void* process_ptr)
const bool show_camera_ui = true;
const bool show_click_button = true;
image_capture_into_t info = { .process = process, .check_qr = ret && check_qr };
- const qr_frame_guides_t qr_frame_guides = check_qr ? QR_GUIDES_LARGE : QR_GUIDES_NONE;
+ const qr_frame_guides_t qr_frame_guides = check_qr ? QR_GUIDES_SHOW : QR_GUIDES_NONE;
jade_camera_process_images(
return_image_data, &info, show_camera_ui, NULL, show_click_button, qr_frame_guides, NULL, NULL);
@@ -172,7 +172,7 @@ void debug_scan_qr_process(void* process_ptr)
// Attempt to scan a qr
qr_data_t qr_data = { .len = 0 };
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_LARGE;
+ const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SHOW;
if (!jade_camera_scan_qr(&qr_data, "Test Scan Image", qr_frame_guides, NULL)) {
JADE_LOGW("QR scanning failed!");
}
diff --git a/main/process/mnemonic.c b/main/process/mnemonic.c
index 5da418a..e140d3a 100644
--- a/main/process/mnemonic.c
+++ b/main/process/mnemonic.c
@@ -207,11 +207,7 @@ static bool mnemonic_export_qr(const char* mnemonic, bool* export_qr_verified)
// Verify QR by scanning it back
qr_data_t qr_data = { .len = 0 };
-#ifdef CONFIG_IDF_TARGET_ESP32S3
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_LARGE;
-#else
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SMALL;
-#endif
+ const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SHOW;
jade_camera_scan_qr(&qr_data, "Scan QR to verify", qr_frame_guides, "blkstrm.com/seedqr");
if (qr_data.len == entropy_len && !memcmp(qr_data.data, entropy, entropy_len)) {
// QR Code scanned, and it matched expected entropy
@@ -1165,11 +1161,7 @@ static bool mnemonic_qr(char* mnemonic, const size_t mnemonic_len)
mnemonic[0] = '\0';
// We return 'true' if we scanned any string data at all
-#ifdef CONFIG_IDF_TARGET_ESP32S3
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_LARGE;
-#else
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SMALL;
-#endif
+ const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SHOW;
const bool qr_scanned
= jade_camera_scan_qr(&qr_data, NULL, qr_frame_guides, "blkstrm.com/scanwallet") && qr_data.len > 0;
if (!qr_scanned) {
diff --git a/main/process/register_otp.c b/main/process/register_otp.c
index 9e5a04c..13ead0f 100644
--- a/main/process/register_otp.c
+++ b/main/process/register_otp.c
@@ -349,11 +349,7 @@ bool register_otp_qr(void)
SENSITIVE_PUSH(&qr_data, sizeof(qr_data));
// Get URI from qr code scan
-#ifdef CONFIG_IDF_TARGET_ESP32S3
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_LARGE;
-#else
- const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SMALL;
-#endif
+ const qr_frame_guides_t qr_frame_guides = QR_GUIDES_SHOW;
if (!jade_camera_scan_qr(&qr_data, NULL, qr_frame_guides, "blkstrm.com/otp") || !qr_data.len) {
// User exit without scanning
JADE_LOGW("No qr code scanned");
diff --git a/main/ui.h b/main/ui.h
index 2a69b8a..b557edd 100644
--- a/main/ui.h
+++ b/main/ui.h
@@ -75,7 +75,7 @@ typedef struct {
} home_menu_entry_t;
// Whether QR Frame Guides (box corners) should be shown
-typedef enum { QR_GUIDES_NONE, QR_GUIDES_SMALL, QR_GUIDES_LARGE } qr_frame_guides_t;
+typedef enum { QR_GUIDES_NONE, QR_GUIDES_SHOW } qr_frame_guides_t;
#define OUTPUT_FLAG_CONFIDENTIAL 1
#define OUTPUT_FLAG_HAS_BLINDING_KEY 2
diff --git a/main/ui/camera.c b/main/ui/camera.c
index 98b3b03..aa49fab 100644
--- a/main/ui/camera.c
+++ b/main/ui/camera.c
@@ -3,20 +3,6 @@
#include "../jade_assert.h"
#include "../ui.h"
-// QR frame guides around the central part of the frame
-// NOTE: these are different for esp32s3(vga) and esp32(qvga)
-#ifdef CONFIG_IDF_TARGET_ESP32S3
-extern const uint8_t icon_qr_large_frame_guide_start[] asm("_binary_icon_qrguide_vga_large_bin_gz_start");
-extern const uint8_t icon_qr_large_frame_guide_end[] asm("_binary_icon_qrguide_vga_large_bin_gz_end");
-extern const uint8_t icon_qr_small_frame_guide_start[] asm("_binary_icon_qrguide_vga_small_bin_gz_start");
-extern const uint8_t icon_qr_small_frame_guide_end[] asm("_binary_icon_qrguide_vga_small_bin_gz_end");
-#else
-extern const uint8_t icon_qr_large_frame_guide_start[] asm("_binary_icon_qrguide_qvga_large_bin_gz_start");
-extern const uint8_t icon_qr_large_frame_guide_end[] asm("_binary_icon_qrguide_qvga_large_bin_gz_end");
-extern const uint8_t icon_qr_small_frame_guide_start[] asm("_binary_icon_qrguide_qvga_small_bin_gz_start");
-extern const uint8_t icon_qr_small_frame_guide_end[] asm("_binary_icon_qrguide_qvga_small_bin_gz_end");
-#endif
-
gui_activity_t* make_camera_activity(gui_view_node_t** image_node, gui_view_node_t** label_node,
const bool show_click_btn, const qr_frame_guides_t qr_frame_guides, progress_bar_t* progress_bar,
const bool show_help_btn)
@@ -38,16 +24,7 @@ gui_activity_t* make_camera_activity(gui_view_node_t** image_node, gui_view_node
// QR frame guide if applicable
if (qr_frame_guides != QR_GUIDES_NONE) {
- Icon* const qr_guide_icon = qr_frame_guides == QR_GUIDES_LARGE
- ? get_icon(icon_qr_large_frame_guide_start, icon_qr_large_frame_guide_end)
- : qr_frame_guides == QR_GUIDES_SMALL
- ? get_icon(icon_qr_small_frame_guide_start, icon_qr_small_frame_guide_end)
- : NULL;
- JADE_ASSERT(qr_guide_icon);
-
- gui_make_icon(&parent, qr_guide_icon, TFT_WHITE, NULL);
- gui_set_icon_animation(parent, qr_guide_icon, 1, 0); // to transfer ownership
- gui_set_align(parent, GUI_ALIGN_CENTER, GUI_ALIGN_MIDDLE);
+ gui_make_qrguide(&parent, TFT_WHITE);
gui_set_parent(parent, *image_node);
}
Why this scored 15/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.