camera: skip every second screen refresh during continuous scanning
What changed, and why it matters
This commit is a performance tweak for the camera preview screen on Blockstream Jade hardware wallets. It skips updating the on-screen preview every second camera frame so the device can analyze more frames per second when scanning QR codes. There is no security-relevant change visible in the code.
No security action required. Treat as a normal performance improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change reorders the camera task loop so that, when there is no ‘click’ button (continuous scanning mode), the user processing callback is invoked on every captured frame and the LCD screen is refreshed only on alternate frames. A counter num_captures toggles skip_screen_update to halve UI redraws. The logic for frames with a click button is unchanged. This is purely a frame-rate/UX optimization.
Changed components
main/camera.ccamera preview / QR scanning taskInspect captured patch +17 / −8
diff --git a/main/camera.c b/main/camera.c
index 9aab331..d3b5d93 100644
--- a/main/camera.c
+++ b/main/camera.c
@@ -371,7 +371,7 @@ static void jade_camera_task(void* data)
}
// camera_config->ctx is optional
- // camera_config->show_ui indicates whether to show a ui or collect cmaera data 'silently'
+ // camera_config->show_ui indicates whether to show a ui or collect camera data 'silently'
// camera_config->text_label is optional
// camera_config->show_click_button indicates we want the user to select the images presented
// (otherwise all images are presented) to the given callback function ctx.fn_process()
@@ -427,6 +427,7 @@ static void jade_camera_task(void* data)
// Loop periodically refreshes screen image from camera, and waits for button event
bool done = false;
+ uint32_t num_captures = 0;
while (!done) {
// Capture camera output
camera_fb_t* const fb = esp_camera_fb_get();
@@ -437,9 +438,22 @@ static void jade_camera_task(void* data)
JADE_ASSERT(fb->format == PIXFORMAT_GRAYSCALE); // 1BPP/GRAYSCALE
JADE_ASSERT(fb->width == CAMERA_IMAGE_WIDTH);
JADE_ASSERT(fb->height == CAMERA_IMAGE_HEIGHT);
+ ++num_captures;
- // If we have a gui, update the image on screen and check for button events
- if (camera_config->show_ui) {
+ bool skip_screen_update = false;
+ if (!camera_config->show_click_button) {
+ // We have no 'click' button (or no gui at all).
+ // Run the processing callback on every frame
+ done = invoke_user_cb_fn(camera_config, fb);
+ if (num_captures == 2) {
+ // Skip every second screen update to scan faster
+ num_captures = 0;
+ skip_screen_update = true;
+ }
+ }
+
+ if (!done && !skip_screen_update && camera_config->show_ui) {
+ // We have a gui. Update the image on screen and check for button events.
// Copy from camera output to screen image
// (Ensure source image large enough to be scaled down to display image size)
JADE_ASSERT(fb->len >= UI2CAM(UI2CAM(image_size))); // x and y scaled
@@ -475,11 +489,6 @@ static void jade_camera_task(void* data)
}
}
- // If we have no 'click' button (or no gui at all), we run the processing callback on every frame
- if (!done && !camera_config->show_click_button) {
- done = invoke_user_cb_fn(camera_config, fb);
- }
-
// Release camera output buffer
esp_camera_fb_return(fb);
}
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.