From 69be9b1edfd798b9f8f06f51e9e3f091148ba21d Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Tue, 24 Jan 2023 14:16:23 +0100 Subject: [PATCH] refactor(core): improve safety of buffer handling [no changelog] --- core/embed/lib/buffers.c | 121 +++++------ core/embed/lib/buffers.h | 27 ++- core/embed/lib/display.c | 26 ++- core/embed/rust/build.rs | 24 ++- core/embed/rust/src/trezorhal/buffers.rs | 173 +++++++-------- core/embed/rust/src/trezorhal/display.rs | 13 +- core/embed/rust/src/trezorhal/dma2d.rs | 22 +- .../rust/src/ui/display/loader/circular.rs | 16 +- core/embed/rust/src/ui/display/mod.rs | 39 ++-- core/embed/rust/src/ui/display/tjpgd.rs | 35 +-- .../ui/model_tt/component/homescreen/mod.rs | 14 +- .../model_tt/component/homescreen/render.rs | 203 +++++++++++------- 12 files changed, 413 insertions(+), 300 deletions(-) diff --git a/core/embed/lib/buffers.c b/core/embed/lib/buffers.c index 0d987a6e7..759d43fc2 100644 --- a/core/embed/lib/buffers.c +++ b/core/embed/lib/buffers.c @@ -22,82 +22,61 @@ #include "fonts/fonts.h" #include "memzero.h" -#define BUFFERS_16BPP 3 -#define BUFFERS_4BPP 3 -#define BUFFERS_TEXT 1 -#define BUFFERS_JPEG 1 -#define BUFFERS_JPEG_WORK 1 -#define BUFFERS_BLURRING 1 - const int32_t text_buffer_height = FONT_MAX_HEIGHT; const int32_t buffer_width = DISPLAY_RESX; -BUFFER_SECTION line_buffer_16bpp_t line_buffers_16bpp[BUFFERS_16BPP]; -BUFFER_SECTION line_buffer_4bpp_t line_buffers_4bpp[BUFFERS_4BPP]; -BUFFER_SECTION buffer_text_t text_buffers[BUFFERS_TEXT]; -NODMA_BUFFER_SECTION buffer_jpeg_t jpeg_buffers[BUFFERS_JPEG]; -NODMA_BUFFER_SECTION buffer_jpeg_work_t jpeg_work_buffers[BUFFERS_JPEG_WORK]; -NODMA_BUFFER_SECTION buffer_blurring_t blurring_buffers[BUFFERS_BLURRING]; +#define CONCAT_(a, b) a##b +#define CONCAT(a, b) CONCAT_(a, b) -line_buffer_16bpp_t* buffers_get_line_buffer_16bpp(uint16_t idx, bool clear) { - if (idx >= BUFFERS_16BPP) { - return NULL; - } - if (clear) { - memzero(&line_buffers_16bpp[idx], sizeof(line_buffers_16bpp[idx])); - } - return &line_buffers_16bpp[idx]; -} +#define CONCAT3_(a, b, c) a##b##c +#define CONCAT3(a, b, c) CONCAT3_(a, b, c) -line_buffer_4bpp_t* buffers_get_line_buffer_4bpp(uint16_t idx, bool clear) { - if (idx >= BUFFERS_4BPP) { - return NULL; - } - if (clear) { - memzero(&line_buffers_4bpp[idx], sizeof(line_buffers_4bpp[idx])); - } - return &line_buffers_4bpp[idx]; -} +#define STRUCT(name) CONCAT3(buffers_, name, _t) +#define TYPE(name) CONCAT3(buffer_, name, _t) +#define FUNCTION(name) CONCAT(buffers_get_, name) +#define FUNCTION_FREE(name) CONCAT(buffers_free_, name) +#define VARNAME(name) CONCAT(buffers_, name) -buffer_text_t* buffers_get_text_buffer(uint16_t idx, bool clear) { - if (idx >= BUFFERS_TEXT) { - return NULL; - } - if (clear) { - memzero(&text_buffers[idx], sizeof(text_buffers[idx])); - } - return &text_buffers[idx]; -} - -buffer_jpeg_t* buffers_get_jpeg_buffer(uint16_t idx, bool clear) { - if (idx >= BUFFERS_JPEG) { - return NULL; +#define BUFFER(section, name, count) \ + typedef struct { \ + TYPE(name) buffers[count]; \ + uint8_t allocated[count]; \ + } STRUCT(name); \ + section STRUCT(name) VARNAME(name); \ + \ + TYPE(name) * FUNCTION(name)(bool clear) { \ + int idx = -1; \ + for (int i = 0; i < (count); i++) { \ + if (VARNAME(name).allocated[i] == 0) { \ + idx = i; \ + break; \ + } \ + } \ + if (idx < 0) { \ + return NULL; \ + } \ + if (clear) { \ + memzero(&VARNAME(name).buffers[idx], \ + sizeof(VARNAME(name).buffers[idx])); \ + } \ + VARNAME(name).allocated[idx] = 1; \ + return &VARNAME(name).buffers[idx]; \ + } \ + void FUNCTION_FREE(name)(TYPE(name) * buffer) { \ + if (buffer == NULL) { \ + return; \ + } \ + for (uint16_t i = 0; i < (count); i++) { \ + if (buffer == &VARNAME(name).buffers[i]) { \ + VARNAME(name).allocated[i] = 0; \ + return; \ + } \ + } \ } - if (clear) { - memzero(&jpeg_buffers[idx], sizeof(jpeg_buffers[idx])); - } - return &jpeg_buffers[idx]; -} - -buffer_jpeg_work_t* buffers_get_jpeg_work_buffer(uint16_t idx, bool clear) { - if (idx >= BUFFERS_JPEG_WORK) { - return NULL; - } - - if (clear) { - memzero(&jpeg_work_buffers[idx], sizeof(jpeg_work_buffers[idx])); - } - return &jpeg_work_buffers[idx]; -} - -buffer_blurring_t* buffers_get_blurring_buffer(uint16_t idx, bool clear) { - if (idx >= BUFFERS_BLURRING) { - return NULL; - } - - if (clear) { - memzero(&blurring_buffers[idx], sizeof(blurring_buffers[idx])); - } - return &blurring_buffers[idx]; -} +BUFFER(BUFFER_SECTION, line_16bpp, 3); +BUFFER(BUFFER_SECTION, line_4bpp, 3); +BUFFER(BUFFER_SECTION, text, 1); +BUFFER(NODMA_BUFFER_SECTION, jpeg, 1); +BUFFER(NODMA_BUFFER_SECTION, jpeg_work, 1); +BUFFER(NODMA_BUFFER_SECTION, blurring, 1); diff --git a/core/embed/lib/buffers.h b/core/embed/lib/buffers.h index 7cbaab649..d58a73159 100644 --- a/core/embed/lib/buffers.h +++ b/core/embed/lib/buffers.h @@ -59,11 +59,11 @@ typedef __attribute__((aligned(4))) struct { uint8_t buffer[LINE_BUFFER_16BPP_SIZE]; -} line_buffer_16bpp_t; +} buffer_line_16bpp_t; typedef __attribute__((aligned(4))) struct { uint8_t buffer[LINE_BUFFER_4BPP_SIZE]; -} line_buffer_4bpp_t; +} buffer_line_4bpp_t; typedef __attribute__((aligned(4))) struct { uint8_t buffer[TEXT_BUFFER_SIZE]; @@ -84,11 +84,22 @@ typedef __attribute__((aligned(4))) struct { extern const int32_t text_buffer_height; extern const int32_t buffer_width; -line_buffer_16bpp_t* buffers_get_line_buffer_16bpp(uint16_t idx, bool clear); -line_buffer_4bpp_t* buffers_get_line_buffer_4bpp(uint16_t idx, bool clear); -buffer_text_t* buffers_get_text_buffer(uint16_t idx, bool clear); -buffer_jpeg_t* buffers_get_jpeg_buffer(uint16_t idx, bool clear); -buffer_jpeg_work_t* buffers_get_jpeg_work_buffer(uint16_t idx, bool clear); -buffer_blurring_t* buffers_get_blurring_buffer(uint16_t idx, bool clear); +buffer_line_16bpp_t* buffers_get_line_16bpp(bool clear); +void buffers_free_line_16bpp(buffer_line_16bpp_t* buffer); + +buffer_line_4bpp_t* buffers_get_line_4bpp(bool clear); +void buffers_free_line_4bpp(buffer_line_4bpp_t* buffer); + +buffer_text_t* buffers_get_text(bool clear); +void buffers_free_text(buffer_text_t* buffer); + +buffer_jpeg_t* buffers_get_jpeg(bool clear); +void buffers_free_jpeg(buffer_jpeg_t* buffer); + +buffer_jpeg_work_t* buffers_get_jpeg_work(bool clear); +void buffers_free_jpeg_work(buffer_jpeg_work_t* buffer); + +buffer_blurring_t* buffers_get_blurring(bool clear); +void buffers_free_blurring(buffer_blurring_t* buffer); #endif // _BUFFERS_H diff --git a/core/embed/lib/display.c b/core/embed/lib/display.c index fcf860874..2235c82d0 100644 --- a/core/embed/lib/display.c +++ b/core/embed/lib/display.c @@ -312,8 +312,13 @@ void display_image(int x, int y, int w, int h, const void *data, struct uzlib_uncomp decomp = {0}; uint8_t decomp_window[UZLIB_WINDOW_SIZE] = {0}; - line_buffer_16bpp_t *b1 = buffers_get_line_buffer_16bpp(0, false); - line_buffer_16bpp_t *b2 = buffers_get_line_buffer_16bpp(1, false); + buffer_line_16bpp_t *b1 = buffers_get_line_16bpp(false); + if (b1 == NULL) return; + buffer_line_16bpp_t *b2 = buffers_get_line_16bpp(false); + if (b2 == NULL) { + buffers_free_line_16bpp(b1); + return; + }; uzlib_prepare(&decomp, decomp_window, data, datalen, b1, w * 2); @@ -321,7 +326,7 @@ void display_image(int x, int y, int w, int h, const void *data, for (int32_t pos = 0; pos < h; pos++) { int32_t pixels = w; - line_buffer_16bpp_t *next_buf = (pos % 2 == 1) ? b1 : b2; + buffer_line_16bpp_t *next_buf = (pos % 2 == 1) ? b1 : b2; decomp.dest = next_buf->buffer; decomp.dest_limit = next_buf->buffer + w * 2; int st = uzlib_uncompress(&decomp); @@ -330,6 +335,8 @@ void display_image(int x, int y, int w, int h, const void *data, dma2d_start(next_buf->buffer, (uint8_t *)DISPLAY_DATA_ADDRESS, pixels); } dma2d_wait_for_transfer(); + buffers_free_line_16bpp(b1); + buffers_free_line_16bpp(b2); } #endif @@ -391,8 +398,13 @@ void display_icon(int x, int y, int w, int h, const void *data, } uint8_t b[DISPLAY_RESX / 2]; - line_buffer_4bpp_t *b1 = buffers_get_line_buffer_4bpp(0, false); - line_buffer_4bpp_t *b2 = buffers_get_line_buffer_4bpp(1, false); + buffer_line_4bpp_t *b1 = buffers_get_line_4bpp(false); + if (b1 == NULL) return; + buffer_line_4bpp_t *b2 = buffers_get_line_4bpp(false); + if (b2 == NULL) { + buffers_free_line_4bpp(b1); + return; + } struct uzlib_uncomp decomp = {0}; uint8_t decomp_window[UZLIB_WINDOW_SIZE] = {0}; @@ -404,7 +416,7 @@ void display_icon(int x, int y, int w, int h, const void *data, int off_x = x < 0 ? -x : 0; for (uint32_t pos = 0; pos < h; pos++) { - line_buffer_4bpp_t *next_buf = (pos % 2 == 0) ? b1 : b2; + buffer_line_4bpp_t *next_buf = (pos % 2 == 0) ? b1 : b2; decomp.dest = b; decomp.dest_limit = b + w / 2; int st = uzlib_uncompress(&decomp); @@ -416,6 +428,8 @@ void display_icon(int x, int y, int w, int h, const void *data, } } dma2d_wait_for_transfer(); + buffers_free_line_4bpp(b1); + buffers_free_line_4bpp(b2); } #endif diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 6df040b79..25bbb45ef 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -328,12 +328,24 @@ fn generate_trezorhal_bindings() { .allowlist_function("dma2d_start_blend") .allowlist_function("dma2d_wait_for_transfer") //buffers - .allowlist_function("buffers_get_line_buffer_16bpp") - .allowlist_function("buffers_get_line_buffer_4bpp") - .allowlist_function("buffers_get_text_buffer") - .allowlist_function("buffers_get_jpeg_buffer") - .allowlist_function("buffers_get_jpeg_work_buffer") - .allowlist_function("buffers_get_blurring_buffer") + .allowlist_function("buffers_get_line_16bpp") + .allowlist_function("buffers_free_line_16bpp") + .allowlist_function("buffers_get_line_4bpp") + .allowlist_function("buffers_free_line_4bpp") + .allowlist_function("buffers_get_text") + .allowlist_function("buffers_free_text") + .allowlist_function("buffers_get_jpeg") + .allowlist_function("buffers_free_jpeg") + .allowlist_function("buffers_get_jpeg_work") + .allowlist_function("buffers_free_jpeg_work") + .allowlist_function("buffers_get_blurring") + .allowlist_function("buffers_free_blurring") + .no_copy("buffer_line_16bpp_t") + .no_copy("buffer_line_4bpp_t") + .no_copy("buffer_text_t") + .no_copy("buffer_jpeg_t") + .no_copy("buffer_jpeg_work_t") + .no_copy("buffer_blurring_t") .allowlist_var("text_buffer_height") .allowlist_var("buffer_width") //usb diff --git a/core/embed/rust/src/trezorhal/buffers.rs b/core/embed/rust/src/trezorhal/buffers.rs index 9b0f67e72..52064493c 100644 --- a/core/embed/rust/src/trezorhal/buffers.rs +++ b/core/embed/rust/src/trezorhal/buffers.rs @@ -1,88 +1,93 @@ +use core::{ + ops::{Deref, DerefMut}, + ptr, +}; + use super::ffi; -pub use ffi::{ - buffer_blurring_t as BlurringBuffer, buffer_text_t as BufferText, - line_buffer_16bpp_t as LineBuffer16Bpp, line_buffer_4bpp_t as LineBuffer4Bpp, -}; +macro_rules! buffer_wrapper { + ($rust_name: ident, $type: ident, $get: ident, $free: ident) => { + pub struct $rust_name(ptr::NonNull); + + impl $rust_name { + pub fn get() -> Self { + unsafe { + let ptr = ffi::$get(false); + Self(unwrap!(ptr::NonNull::new(ptr))) + } + } + + pub fn get_cleared() -> Self { + unsafe { + let ptr = ffi::$get(true); + Self(unwrap!(ptr::NonNull::new(ptr))) + } + } + } + + impl Deref for $rust_name { + type Target = ffi::$type; + + fn deref(&self) -> &Self::Target { + // SAFETY: The lifetime of the pointer is 'static and the C API + // promises that we are the sole owner. + unsafe { self.0.as_ref() } + } + } + + impl DerefMut for $rust_name { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: The lifetime of the pointer is 'static and the C API + // promises that we are the sole owner, and we have borrowed mutably. + unsafe { self.0.as_mut() } + } + } + + impl Drop for $rust_name { + fn drop(&mut self) { + unsafe { + ffi::$free(self.0.as_mut()); + } + } + } + }; +} + +buffer_wrapper!( + BufferLine16bpp, + buffer_line_16bpp_t, + buffers_get_line_16bpp, + buffers_free_line_16bpp +); +buffer_wrapper!( + BufferLine4bpp, + buffer_line_4bpp_t, + buffers_get_line_4bpp, + buffers_free_line_4bpp +); +buffer_wrapper!( + BufferText, + buffer_text_t, + buffers_get_text, + buffers_free_text +); +buffer_wrapper!( + BufferBlurring, + buffer_blurring_t, + buffers_get_blurring, + buffers_free_blurring +); #[cfg(feature = "jpeg")] -pub use ffi::{buffer_jpeg_t as BufferJpeg, buffer_jpeg_work_t as BufferJpegWork}; - -/// Returns a buffer for one line of 16bpp data -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times -pub unsafe fn get_buffer_16bpp(idx: u16, clear: bool) -> &'static mut LineBuffer16Bpp { - unsafe { - let ptr = ffi::buffers_get_line_buffer_16bpp(idx, clear); - unwrap!(ptr.as_mut()) - } -} - -/// Returns a buffer for one line of 4bpp data -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times -pub unsafe fn get_buffer_4bpp(idx: u16, clear: bool) -> &'static mut LineBuffer4Bpp { - unsafe { - let ptr = ffi::buffers_get_line_buffer_4bpp(idx, clear); - unwrap!(ptr.as_mut()) - } -} - -/// Returns a buffer for one line of text -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times -pub unsafe fn get_text_buffer(idx: u16, clear: bool) -> &'static mut BufferText { - unsafe { - let ptr = ffi::buffers_get_text_buffer(idx, clear); - unwrap!(ptr.as_mut()) - } -} - -/// Returns a buffer for jpeg data -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times +buffer_wrapper!( + BufferJpeg, + buffer_jpeg_t, + buffers_get_jpeg, + buffers_free_jpeg +); #[cfg(feature = "jpeg")] -pub unsafe fn get_jpeg_buffer(idx: u16, clear: bool) -> &'static mut BufferJpeg { - unsafe { - let ptr = ffi::buffers_get_jpeg_buffer(idx, clear); - unwrap!(ptr.as_mut()) - } -} - -/// Returns a jpeg work buffer -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times -#[cfg(feature = "jpeg")] -pub unsafe fn get_jpeg_work_buffer(idx: u16, clear: bool) -> &'static mut BufferJpegWork { - unsafe { - let ptr = ffi::buffers_get_jpeg_work_buffer(idx, clear); - unwrap!(ptr.as_mut()) - } -} - -/// Returns a buffer for blurring data -/// -/// # Safety -/// -/// This function is unsafe because the caller has to guarantee -/// that he doesn't use buffer on same index multiple times -pub unsafe fn get_blurring_buffer(idx: u16, clear: bool) -> &'static mut BlurringBuffer { - unsafe { - let ptr = ffi::buffers_get_blurring_buffer(idx, clear); - unwrap!(ptr.as_mut()) - } -} +buffer_wrapper!( + BufferJpegWork, + buffer_jpeg_work_t, + buffers_get_jpeg_work, + buffers_free_jpeg_work +); diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index a90278ef2..fecb5aa62 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -1,5 +1,5 @@ use super::ffi; -use core::ptr; +use core::{ops::DerefMut, ptr}; use cty::c_int; use crate::trezorhal::buffers::BufferText; @@ -36,7 +36,7 @@ pub fn text_into_buffer(text: &str, font: i32, buffer: &mut BufferText, x_offset text.as_ptr() as _, text.len() as _, font, - buffer as _, + buffer.deref_mut(), x_offset.into(), ) } @@ -92,7 +92,14 @@ pub fn bar_radius(x: i16, y: i16, w: i16, h: i16, fgcolor: u16, bgcolor: u16, ra pub fn bar_radius_buffer(x: i16, y: i16, w: i16, h: i16, radius: u8, buffer: &mut BufferText) { unsafe { - ffi::display_bar_radius_buffer(x.into(), y.into(), w.into(), h.into(), radius, buffer as _) + ffi::display_bar_radius_buffer( + x.into(), + y.into(), + w.into(), + h.into(), + radius, + buffer.deref_mut(), + ) } } diff --git a/core/embed/rust/src/trezorhal/dma2d.rs b/core/embed/rust/src/trezorhal/dma2d.rs index 0de4daa17..0d4047719 100644 --- a/core/embed/rust/src/trezorhal/dma2d.rs +++ b/core/embed/rust/src/trezorhal/dma2d.rs @@ -12,7 +12,16 @@ pub fn dma2d_setup_4bpp_over_16bpp(overlay_color: u16) { unsafe { ffi::dma2d_setup_4bpp_over_16bpp(overlay_color) } } -pub fn dma2d_start(buffer: &[u8], pixels: i16) { +/// Starts dma2d transfer +/// +/// # Safety +/// +/// This function is unsafe because the caller has to guarantee that he: +/// 1) doesn't mutate the buffers until the transfer is finished, which is +/// guaranteed by calling `dma2d_wait_for_transfer` +/// 2) the buffer doesn't get dropped until the transfer is finished +/// 3) doesn't call this function while another transfer is running +pub unsafe fn dma2d_start(buffer: &[u8], pixels: i16) { unsafe { ffi::dma2d_start( buffer.as_ptr() as _, @@ -22,7 +31,16 @@ pub fn dma2d_start(buffer: &[u8], pixels: i16) { } } -pub fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels: i16) { +/// Starts blending +/// +/// # Safety +/// +/// This function is unsafe because the caller has to guarantee that he: +/// 1) doesn't mutate the buffers until the transfer is finished, which is +/// guaranteed by calling `dma2d_wait_for_transfer` +/// 2) the buffer doesn't get dropped until the transfer is finished +/// 3) doesn't call this function while another transfer is running +pub unsafe fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels: i16) { unsafe { ffi::dma2d_start_blend( overlay_buffer.as_ptr() as _, diff --git a/core/embed/rust/src/ui/display/loader/circular.rs b/core/embed/rust/src/ui/display/loader/circular.rs index f547433dd..894974a8a 100644 --- a/core/embed/rust/src/ui/display/loader/circular.rs +++ b/core/embed/rust/src/ui/display/loader/circular.rs @@ -8,7 +8,7 @@ use crate::ui::{ #[cfg(feature = "dma2d")] use crate::trezorhal::{ - buffers::{get_buffer_16bpp, get_buffer_4bpp}, + buffers, dma2d::{dma2d_setup_4bpp_over_4bpp, dma2d_start_blend, dma2d_wait_for_transfer}, }; @@ -353,11 +353,11 @@ pub fn loader_rust( let n_start = Point::new(-start_vector.y, start_vector.x); - let b1 = unsafe { get_buffer_16bpp(0, false) }; - let b2 = unsafe { get_buffer_16bpp(1, false) }; - let ib1 = unsafe { get_buffer_4bpp(0, true) }; - let ib2 = unsafe { get_buffer_4bpp(1, true) }; - let empty_line = unsafe { get_buffer_4bpp(2, true) }; + let mut b1 = buffers::BufferLine16bpp::get(); + let mut b2 = buffers::BufferLine16bpp::get(); + let mut ib1 = buffers::BufferLine4bpp::get_cleared(); + let mut ib2 = buffers::BufferLine4bpp::get_cleared(); + let mut empty_line = buffers::BufferLine4bpp::get_cleared(); dma2d_setup_4bpp_over_4bpp(fg_color.into(), bg_color.into(), icon_color.into()); @@ -420,7 +420,9 @@ pub fn loader_rust( } dma2d_wait_for_transfer(); - dma2d_start_blend(&icon_buffer.buffer, &loader_buffer.buffer, clamped.width()); + unsafe { + dma2d_start_blend(&icon_buffer.buffer, &loader_buffer.buffer, clamped.width()); + } } dma2d_wait_for_transfer(); diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index a61c38ff2..44a1df1af 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -9,7 +9,6 @@ use super::{ }; #[cfg(feature = "dma2d")] use crate::trezorhal::{ - buffers::{get_buffer_16bpp, get_buffer_4bpp}, dma2d::{ dma2d_setup_4bpp_over_16bpp, dma2d_setup_4bpp_over_4bpp, dma2d_start_blend, dma2d_wait_for_transfer, @@ -21,7 +20,7 @@ use crate::ui::geometry::TOP_LEFT; use crate::{ time::Duration, - trezorhal::{buffers::get_text_buffer, display, time, uzlib::UzlibContext}, + trezorhal::{buffers, display, time, uzlib::UzlibContext}, ui::lerp::Lerp, }; use core::slice; @@ -448,13 +447,13 @@ pub fn text_over_image( offset_text: Offset, text_color: Color, ) { - let text_buffer = unsafe { get_text_buffer(0, true) }; - let img1 = unsafe { get_buffer_16bpp(0, true) }; - let img2 = unsafe { get_buffer_16bpp(1, true) }; - let empty_img = unsafe { get_buffer_16bpp(2, true) }; - let t1 = unsafe { get_buffer_4bpp(0, true) }; - let t2 = unsafe { get_buffer_4bpp(1, true) }; - let empty_t = unsafe { get_buffer_4bpp(2, true) }; + let mut text_buffer = buffers::BufferText::get(); + let mut img1 = buffers::BufferLine16bpp::get_cleared(); + let mut img2 = buffers::BufferLine16bpp::get_cleared(); + let mut empty_img = buffers::BufferLine16bpp::get_cleared(); + let mut t1 = buffers::BufferLine4bpp::get_cleared(); + let mut t2 = buffers::BufferLine4bpp::get_cleared(); + let mut empty_t = buffers::BufferLine4bpp::get_cleared(); let r_img; let area; @@ -495,7 +494,7 @@ pub fn text_over_image( Point::new(text_right, text_bottom), ); - display::text_into_buffer(text, font.into(), text_buffer, 0); + display::text_into_buffer(text, font.into(), &mut text_buffer, 0); set_window(clamped); @@ -548,7 +547,7 @@ pub fn text_over_image( } dma2d_wait_for_transfer(); - dma2d_start_blend(&t_buffer.buffer, &img_buffer.buffer, clamped.width()); + unsafe { dma2d_start_blend(&t_buffer.buffer, &img_buffer.buffer, clamped.width()) }; } dma2d_wait_for_transfer(); @@ -572,12 +571,12 @@ pub fn icon_over_icon( fg: (Icon, Offset, Color), bg_color: Color, ) { - let bg1 = unsafe { get_buffer_16bpp(0, true) }; - let bg2 = unsafe { get_buffer_16bpp(1, true) }; - let empty1 = unsafe { get_buffer_16bpp(2, true) }; - let fg1 = unsafe { get_buffer_4bpp(0, true) }; - let fg2 = unsafe { get_buffer_4bpp(1, true) }; - let empty2 = unsafe { get_buffer_4bpp(2, true) }; + let mut bg1 = buffers::BufferLine16bpp::get_cleared(); + let mut bg2 = buffers::BufferLine16bpp::get_cleared(); + let mut empty1 = buffers::BufferLine16bpp::get_cleared(); + let mut fg1 = buffers::BufferLine4bpp::get_cleared(); + let mut fg2 = buffers::BufferLine4bpp::get_cleared(); + let mut empty2 = buffers::BufferLine4bpp::get_cleared(); let (icon_bg, offset_bg, color_icon_bg) = bg; let (icon_fg, offset_fg, color_icon_fg) = fg; @@ -662,7 +661,7 @@ pub fn icon_over_icon( } dma2d_wait_for_transfer(); - dma2d_start_blend(&fg_buffer.buffer, &bg_buffer.buffer, clamped.width()); + unsafe { dma2d_start_blend(&fg_buffer.buffer, &bg_buffer.buffer, clamped.width()) }; } dma2d_wait_for_transfer(); @@ -768,13 +767,13 @@ pub fn bar_with_text_and_fill( } pub fn marquee(area: Rect, text: &str, offset: i16, font: Font, fg: Color, bg: Color) { - let buffer = unsafe { get_text_buffer(0, true) }; + let mut buffer = buffers::BufferText::get_cleared(); let area = area.translate(get_offset()); let clamped = area.clamp(constant::screen()); set_window(clamped); - display::text_into_buffer(text, font.into(), buffer, offset); + display::text_into_buffer(text, font.into(), &mut buffer, offset); let tbl = get_color_table(fg, bg); for y in 0..clamped.height() { diff --git a/core/embed/rust/src/ui/display/tjpgd.rs b/core/embed/rust/src/ui/display/tjpgd.rs index bfb84e349..c85971de4 100644 --- a/core/embed/rust/src/ui/display/tjpgd.rs +++ b/core/embed/rust/src/ui/display/tjpgd.rs @@ -35,7 +35,7 @@ Trezor modifications: use crate::{ trezorhal::{ - buffers::{get_jpeg_work_buffer, BufferJpeg}, + buffers::{BufferJpeg, BufferJpegWork}, display::pixeldata, }, ui::{ @@ -1399,7 +1399,8 @@ impl<'i, 'p> JDEC<'i, 'p> { } pub fn jpeg(data: &[u8], pos: Point, scale: u8) { - let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut buffer = BufferJpegWork::get_cleared(); + let pool = buffer.buffer.as_mut_slice(); let mut out = PixelDataOutput(pos); let mut inp = BufferInput(data); if let Ok(mut jd) = JDEC::new(&mut inp, pool) { @@ -1409,9 +1410,10 @@ pub fn jpeg(data: &[u8], pos: Point, scale: u8) { } pub fn jpeg_info(data: &[u8]) -> Option<(Offset, i16)> { - let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut buffer = BufferJpegWork::get_cleared(); + let pool = buffer.buffer.as_mut_slice(); let mut inp = BufferInput(data); - if let Ok(jd) = JDEC::new(&mut inp, pool) { + let result = if let Ok(jd) = JDEC::new(&mut inp, pool) { let mcu_height = jd.mcu_height(); if mcu_height > 16 { return None; @@ -1419,13 +1421,15 @@ pub fn jpeg_info(data: &[u8]) -> Option<(Offset, i16)> { Some((Offset::new(jd.width(), jd.height()), mcu_height)) } else { None - } + }; + result } pub fn jpeg_test(data: &[u8]) -> bool { - let pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() }; + let mut buffer = BufferJpegWork::get_cleared(); + let pool = buffer.buffer.as_mut_slice(); let mut inp = BufferInput(data); - if let Ok(mut jd) = JDEC::new(&mut inp, pool) { + let result = if let Ok(mut jd) = JDEC::new(&mut inp, pool) { if jd.mcu_height() > 16 { return false; } @@ -1438,7 +1442,8 @@ pub fn jpeg_test(data: &[u8]) -> bool { res.is_ok() } else { false - } + }; + result } pub trait JpegInput { @@ -1464,18 +1469,18 @@ pub trait JpegOutput { fn write(&mut self, jd: &JDEC, rect: Rect, pixels: &[u16]) -> bool; } -pub struct BufferOutput<'o> { - buffer: &'o mut BufferJpeg, +pub struct BufferOutput { + buffer: BufferJpeg, buffer_width: i16, buffer_height: i16, current_line: i16, current_line_pix: i16, } -impl<'o> BufferOutput<'o> { - pub fn new(buffer: &'o mut BufferJpeg, buffer_width: i16, buffer_height: i16) -> Self { +impl BufferOutput { + pub fn new(buffer_width: i16, buffer_height: i16) -> Self { Self { - buffer, + buffer: BufferJpeg::get_cleared(), buffer_width, buffer_height, current_line: 0, @@ -1484,11 +1489,11 @@ impl<'o> BufferOutput<'o> { } pub fn buffer(&mut self) -> &mut BufferJpeg { - self.buffer + &mut self.buffer } } -impl<'o> JpegOutput for BufferOutput<'o> { +impl JpegOutput for BufferOutput { fn write(&mut self, jd: &JDEC, rect: Rect, bitmap: &[u16]) -> bool { let w = rect.width(); let h = rect.height(); diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs index 5f3f59df8..facdd9ae3 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs @@ -15,7 +15,7 @@ use crate::{ }; use crate::{ - trezorhal::{display::ToifFormat, uzlib::UZLIB_WINDOW_SIZE}, + trezorhal::{buffers::BufferJpegWork, display::ToifFormat, uzlib::UZLIB_WINDOW_SIZE}, ui::{ constant::HEIGHT, display::{tjpgd::BufferInput, toif::Toif}, @@ -211,7 +211,8 @@ where if let Ok(data) = res { if is_image_jpeg(data.as_ref()) { let mut input = BufferInput(data.as_ref()); - let mut hs_img = HomescreenJpeg::new(&mut input); + let mut pool = BufferJpegWork::get_cleared(); + let mut hs_img = HomescreenJpeg::new(&mut input, pool.buffer.as_mut_slice()); homescreen( &mut hs_img, &[text], @@ -236,7 +237,8 @@ where if show_default { let mut input = BufferInput(IMAGE_HOMESCREEN); - let mut hs_img = HomescreenJpeg::new(&mut input); + let mut pool = BufferJpegWork::get_cleared(); + let mut hs_img = HomescreenJpeg::new(&mut input, pool.buffer.as_mut_slice()); homescreen( &mut hs_img, &[text], @@ -328,7 +330,8 @@ where if let Ok(data) = res { if is_image_jpeg(data.as_ref()) { let mut input = BufferInput(data.as_ref()); - let mut hs_img = HomescreenJpeg::new(&mut input); + let mut pool = BufferJpegWork::get_cleared(); + let mut hs_img = HomescreenJpeg::new(&mut input, pool.buffer.as_mut_slice()); homescreen_blurred(&mut hs_img, &texts); show_default = false; } else if is_image_toif(data.as_ref()) { @@ -343,7 +346,8 @@ where if show_default { let mut input = BufferInput(IMAGE_HOMESCREEN); - let mut hs_img = HomescreenJpeg::new(&mut input); + let mut pool = BufferJpegWork::get_cleared(); + let mut hs_img = HomescreenJpeg::new(&mut input, pool.buffer.as_mut_slice()); homescreen_blurred(&mut hs_img, &texts); } } diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs index b4d5df193..fb85e46d2 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs @@ -1,33 +1,25 @@ -#[cfg(feature = "dma2d")] -use crate::trezorhal::{ - buffers::{get_buffer_16bpp, get_buffer_4bpp, get_text_buffer, BufferText, LineBuffer4Bpp}, - dma2d::{dma2d_setup_4bpp_over_16bpp, dma2d_start_blend, dma2d_wait_for_transfer}, -}; use crate::{ trezorhal::{ - buffers::{get_blurring_buffer, get_jpeg_buffer, get_jpeg_work_buffer, BufferJpeg}, + buffers::{BufferBlurring, BufferJpeg, BufferLine16bpp, BufferLine4bpp, BufferText}, display, display::bar_radius_buffer, + dma2d::{dma2d_setup_4bpp_over_16bpp, dma2d_start_blend, dma2d_wait_for_transfer}, uzlib::UzlibContext, }, ui::{ - constant::screen, - display::{position_buffer, set_window, Color}, + component::text::TextStyle, + constant::{screen, HEIGHT, WIDTH}, + display::{ + position_buffer, set_window, + tjpgd::{BufferInput, BufferOutput, JDEC}, + Color, Icon, + }, geometry::{Offset, Point, Rect}, + model_tt::theme, + util::icon_text_center, }, }; -use crate::ui::{ - component::text::TextStyle, - constant::{HEIGHT, WIDTH}, - display::{ - tjpgd::{BufferInput, BufferOutput, JDEC}, - Icon, - }, - model_tt::theme, - util::icon_text_center, -}; - #[derive(Clone, Copy)] pub struct HomescreenText<'a> { pub text: &'a str, @@ -91,18 +83,15 @@ pub trait HomescreenDecompressor { } pub struct HomescreenJpeg<'i> { - pub output: BufferOutput<'i>, + pub output: BufferOutput, pub jdec: Option>, } impl<'i> HomescreenJpeg<'i> { - pub fn new(input: &'i mut BufferInput<'i>) -> Self { + pub fn new(input: &'i mut BufferInput<'i>, pool: &'i mut [u8]) -> Self { Self { - output: BufferOutput::new(unsafe { get_jpeg_buffer(0, true) }, WIDTH, 16), - jdec: JDEC::new(input, unsafe { - get_jpeg_work_buffer(0, true).buffer.as_mut_slice() - }) - .ok(), + output: BufferOutput::new(WIDTH, 16), + jdec: JDEC::new(input, pool).ok(), } } } @@ -125,7 +114,7 @@ impl<'i> HomescreenDecompressor for HomescreenJpeg<'i> { } pub struct HomescreenToif<'i> { - pub output: BufferOutput<'i>, + pub output: BufferOutput, pub decomp_context: UzlibContext<'i>, line: i16, } @@ -133,7 +122,7 @@ pub struct HomescreenToif<'i> { impl<'i> HomescreenToif<'i> { pub fn new(context: UzlibContext<'i>) -> Self { Self { - output: BufferOutput::new(unsafe { get_jpeg_buffer(0, true) }, WIDTH, 16), + output: BufferOutput::new(WIDTH, 16), decomp_context: context, line: 0, } @@ -184,7 +173,7 @@ fn homescreen_get_fg_text( y_tmp: i16, text_info: HomescreenTextInfo, text_buffer: &BufferText, - fg_buffer: &mut LineBuffer4Bpp, + fg_buffer: &mut BufferLine4bpp, ) -> bool { if y_tmp >= text_info.text_area.y0 && y_tmp < text_info.text_area.y1 { let y_pos = y_tmp - text_info.text_area.y0; @@ -204,7 +193,7 @@ fn homescreen_get_fg_icon( y_tmp: i16, text_info: HomescreenTextInfo, icon_data: &[u8], - fg_buffer: &mut LineBuffer4Bpp, + fg_buffer: &mut BufferLine4bpp, ) { if let Some(icon_area) = text_info.icon_area { let icon_size = icon_area.size(); @@ -291,13 +280,13 @@ fn homescreen_dim_area(x: i16, y: i16) -> bool { fn homescreen_line_blurred( icon_data: &[u8], text_buffer: &mut BufferText, + fg_buffer: &mut BufferLine4bpp, + img_buffer: &mut BufferLine16bpp, text_info: HomescreenTextInfo, blurring: &BlurringContext, y: i16, ) -> bool { - let t_buffer = unsafe { get_buffer_4bpp((y & 0x1) as u16, true) }; - let mut img_buffer = unsafe { get_buffer_16bpp((y & 0x1) as u16, false) }; - + fg_buffer.buffer.fill(0); for x in 0..HOMESCREEN_IMAGE_WIDTH { let c = if LOCKSCREEN_DIM_ALL { let x = x as usize; @@ -327,28 +316,31 @@ fn homescreen_line_blurred( img_buffer.buffer[j] = (c & 0xFF) as u8; } - let done = homescreen_get_fg_text(y, text_info, text_buffer, t_buffer); - homescreen_get_fg_icon(y, text_info, icon_data, t_buffer); + let done = homescreen_get_fg_text(y, text_info, text_buffer, fg_buffer); + homescreen_get_fg_icon(y, text_info, icon_data, fg_buffer); dma2d_wait_for_transfer(); dma2d_setup_4bpp_over_16bpp(text_info.text_color.into()); - dma2d_start_blend(&t_buffer.buffer, &img_buffer.buffer, WIDTH); + unsafe { + dma2d_start_blend(&fg_buffer.buffer, &img_buffer.buffer, WIDTH); + } done } +#[allow(clippy::too_many_arguments)] fn homescreen_line( icon_data: &[u8], text_buffer: &mut BufferText, text_info: HomescreenTextInfo, data_buffer: &mut BufferJpeg, + fg_buffer: &mut BufferLine4bpp, + img_buffer: &mut BufferLine16bpp, mcu_height: i16, y: i16, ) -> bool { - let t_buffer = unsafe { get_buffer_4bpp((y & 0x1) as u16, true) }; - let mut img_buffer = unsafe { get_buffer_16bpp((y & 0x1) as u16, false) }; - let image_data = get_data(data_buffer, y, mcu_height); + fg_buffer.buffer.fill(0); for x in 0..HOMESCREEN_IMAGE_WIDTH { let d = image_data[x as usize]; @@ -373,12 +365,14 @@ fn homescreen_line( img_buffer.buffer[j] = (c & 0xFF) as u8; } - let done = homescreen_get_fg_text(y, text_info, text_buffer, t_buffer); - homescreen_get_fg_icon(y, text_info, icon_data, t_buffer); + let done = homescreen_get_fg_text(y, text_info, text_buffer, fg_buffer); + homescreen_get_fg_icon(y, text_info, icon_data, fg_buffer); dma2d_wait_for_transfer(); dma2d_setup_4bpp_over_16bpp(text_info.text_color.into()); - dma2d_start_blend(&t_buffer.buffer, &img_buffer.buffer, WIDTH); + unsafe { + dma2d_start_blend(&fg_buffer.buffer, &img_buffer.buffer, WIDTH); + } done } @@ -427,7 +421,7 @@ fn update_accs_sub(data: &[u16], idx: usize, acc_r: &mut u16, acc_g: &mut u16, a } struct BlurringContext { - pub lines: &'static mut [[[u16; 240usize]; 3usize]], + mem: BufferBlurring, pub totals: [[u16; HOMESCREEN_IMAGE_WIDTH as usize]; COLORS], line_num: i16, add_idx: usize, @@ -436,9 +430,8 @@ struct BlurringContext { impl BlurringContext { pub fn new() -> Self { - let mem = unsafe { get_blurring_buffer(0, true) }; Self { - lines: &mut mem.buffer[0..DECOMP_LINES], + mem: BufferBlurring::get_cleared(), totals: [[0; HOMESCREEN_IMAGE_WIDTH as usize]; COLORS], line_num: 0, add_idx: 0, @@ -447,8 +440,9 @@ impl BlurringContext { } fn clear(&mut self) { + let lines = &mut self.mem.buffer[0..DECOMP_LINES]; for (i, total) in self.totals.iter_mut().enumerate() { - for line in self.lines.iter_mut() { + for line in lines.iter_mut() { line[i].fill(0); } total.fill(0); @@ -457,6 +451,7 @@ impl BlurringContext { // computes color averages for one line of image data fn compute_line_avgs(&mut self, buffer: &mut BufferJpeg, mcu_height: i16) { + let lines = &mut self.mem.buffer[0..DECOMP_LINES]; let mut acc_r = 0; let mut acc_g = 0; let mut acc_b = 0; @@ -468,9 +463,9 @@ impl BlurringContext { } for i in 0..HOMESCREEN_IMAGE_WIDTH { - self.lines[self.add_idx][RED_IDX][i as usize] = acc_r; - self.lines[self.add_idx][GREEN_IDX][i as usize] = acc_g; - self.lines[self.add_idx][BLUE_IDX][i as usize] = acc_b; + lines[self.add_idx][RED_IDX][i as usize] = acc_r; + lines[self.add_idx][GREEN_IDX][i as usize] = acc_g; + lines[self.add_idx][BLUE_IDX][i as usize] = acc_b; // clamping handles left and right edges let ic = (i - BLUR_RADIUS).clamp(0, HOMESCREEN_IMAGE_WIDTH - 1) as usize; @@ -484,22 +479,24 @@ impl BlurringContext { // adds one line of averages to sliding total averages fn vertical_avg_add(&mut self) { + let lines = &mut self.mem.buffer[0..DECOMP_LINES]; for i in 0..HOMESCREEN_IMAGE_WIDTH as usize { - self.totals[RED_IDX][i] += self.lines[self.add_idx][RED_IDX][i]; - self.totals[GREEN_IDX][i] += self.lines[self.add_idx][GREEN_IDX][i]; - self.totals[BLUE_IDX][i] += self.lines[self.add_idx][BLUE_IDX][i]; + self.totals[RED_IDX][i] += lines[self.add_idx][RED_IDX][i]; + self.totals[GREEN_IDX][i] += lines[self.add_idx][GREEN_IDX][i]; + self.totals[BLUE_IDX][i] += lines[self.add_idx][BLUE_IDX][i]; } } // adds one line and removes one line of averages to/from sliding total averages fn vertical_avg(&mut self) { + let lines = &mut self.mem.buffer[0..DECOMP_LINES]; for i in 0..HOMESCREEN_IMAGE_WIDTH as usize { self.totals[RED_IDX][i] += - self.lines[self.add_idx][RED_IDX][i] - self.lines[self.rem_idx][RED_IDX][i]; + lines[self.add_idx][RED_IDX][i] - lines[self.rem_idx][RED_IDX][i]; self.totals[GREEN_IDX][i] += - self.lines[self.add_idx][GREEN_IDX][i] - self.lines[self.rem_idx][GREEN_IDX][i]; + lines[self.add_idx][GREEN_IDX][i] - lines[self.rem_idx][GREEN_IDX][i]; self.totals[BLUE_IDX][i] += - self.lines[self.add_idx][BLUE_IDX][i] - self.lines[self.rem_idx][BLUE_IDX][i]; + lines[self.add_idx][BLUE_IDX][i] - lines[self.rem_idx][BLUE_IDX][i]; } } @@ -532,11 +529,15 @@ fn get_data(buffer: &mut BufferJpeg, line_num: i16, mcu_height: i16) -> &mut [u1 pub fn homescreen_blurred(data: &mut dyn HomescreenDecompressor, texts: &[HomescreenText]) { let mut icon_data = [0_u8; (HOMESCREEN_MAX_ICON_SIZE * HOMESCREEN_MAX_ICON_SIZE / 2) as usize]; - let text_buffer = unsafe { get_text_buffer(0, true) }; + let mut text_buffer = BufferText::get_cleared(); + let mut fg_buffer_0 = BufferLine4bpp::get_cleared(); + let mut img_buffer_0 = BufferLine16bpp::get_cleared(); + let mut fg_buffer_1 = BufferLine4bpp::get_cleared(); + let mut img_buffer_1 = BufferLine16bpp::get_cleared(); let mut next_text_idx = 1; let mut text_info = - homescreen_position_text(unwrap!(texts.get(0)), text_buffer, &mut icon_data); + homescreen_position_text(unwrap!(texts.get(0)), &mut text_buffer, &mut icon_data); let mcu_height = data.get_height(); data.decompress(); @@ -571,11 +572,36 @@ pub fn homescreen_blurred(data: &mut dyn HomescreenDecompressor, texts: &[Homesc blurring.compute_line_avgs(data.get_data(), mcu_height); } - let done = homescreen_line_blurred(&icon_data, text_buffer, text_info, &blurring, y); + let done = if y % 2 == 0 { + homescreen_line_blurred( + &icon_data, + &mut text_buffer, + &mut fg_buffer_0, + &mut img_buffer_0, + text_info, + &blurring, + y, + ) + } else { + homescreen_line_blurred( + &icon_data, + &mut text_buffer, + &mut fg_buffer_1, + &mut img_buffer_1, + text_info, + &blurring, + y, + ) + }; if done { - (text_info, next_text_idx) = - homescreen_next_text(texts, text_buffer, &mut icon_data, text_info, next_text_idx); + (text_info, next_text_idx) = homescreen_next_text( + texts, + &mut text_buffer, + &mut icon_data, + text_info, + next_text_idx, + ); } blurring.vertical_avg(); @@ -613,7 +639,11 @@ pub fn homescreen( ) { let mut icon_data = [0_u8; (HOMESCREEN_MAX_ICON_SIZE * HOMESCREEN_MAX_ICON_SIZE / 2) as usize]; - let text_buffer = unsafe { get_text_buffer(0, true) }; + let mut text_buffer = BufferText::get_cleared(); + let mut fg_buffer_0 = BufferLine4bpp::get_cleared(); + let mut img_buffer_0 = BufferLine16bpp::get_cleared(); + let mut fg_buffer_1 = BufferLine4bpp::get_cleared(); + let mut img_buffer_1 = BufferLine16bpp::get_cleared(); let mut next_text_idx = 0; let mut text_info = if let Some(notification) = notification { @@ -623,7 +653,7 @@ pub fn homescreen( WIDTH - NOTIFICATION_BORDER * 2, NOTIFICATION_HEIGHT, 2, - text_buffer, + &mut text_buffer, ); let area = Rect::new( Point::new(0, NOTIFICATION_BORDER), @@ -637,7 +667,7 @@ pub fn homescreen( } } else { next_text_idx += 1; - homescreen_position_text(unwrap!(texts.get(0)), text_buffer, &mut icon_data) + homescreen_position_text(unwrap!(texts.get(0)), &mut text_buffer, &mut icon_data) }; set_window(screen()); @@ -649,14 +679,29 @@ pub fn homescreen( data.decompress(); } - let done = homescreen_line( - &icon_data, - text_buffer, - text_info, - data.get_data(), - mcu_height, - y, - ); + let done = if y % 2 == 0 { + homescreen_line( + &icon_data, + &mut text_buffer, + text_info, + data.get_data(), + &mut fg_buffer_0, + &mut img_buffer_0, + mcu_height, + y, + ) + } else { + homescreen_line( + &icon_data, + &mut text_buffer, + text_info, + data.get_data(), + &mut fg_buffer_1, + &mut img_buffer_1, + mcu_height, + y, + ) + }; if done { if notification.is_some() && next_text_idx == 0 { @@ -670,6 +715,9 @@ pub fn homescreen( dma2d_wait_for_transfer(); + drop(fg_buffer_0); + drop(fg_buffer_1); + icon_text_center( text_info.text_area.center(), notification.icon, @@ -678,6 +726,10 @@ pub fn homescreen( style, Offset::new(1, -2), ); + + fg_buffer_0 = BufferLine4bpp::get_cleared(); + fg_buffer_1 = BufferLine4bpp::get_cleared(); + set_window( screen() .split_top(NOTIFICATION_HEIGHT + NOTIFICATION_BORDER) @@ -690,8 +742,13 @@ pub fn homescreen( return; } - (text_info, next_text_idx) = - homescreen_next_text(texts, text_buffer, &mut icon_data, text_info, next_text_idx); + (text_info, next_text_idx) = homescreen_next_text( + texts, + &mut text_buffer, + &mut icon_data, + text_info, + next_text_idx, + ); } } dma2d_wait_for_transfer();