1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00

refactor(core): improve safety of buffer handling

[no changelog]
This commit is contained in:
tychovrahe 2023-01-24 14:16:23 +01:00 committed by TychoVrahe
parent 640685a41f
commit 69be9b1edf
12 changed files with 413 additions and 300 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<ffi::$type>);
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
);

View File

@ -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(),
)
}
}

View File

@ -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 _,

View File

@ -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();

View File

@ -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() {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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<JDEC<'i, 'i>>,
}
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();