diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index db2c69cdc0..134ee5b5c1 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -668,7 +668,7 @@ static uint8_t convert_char(const uint8_t c) { return 0; } -static const uint8_t *get_glyph(int font, uint8_t c) { +const uint8_t *display_get_glyph(int font, uint8_t c) { c = convert_char(c); if (!c) return 0; @@ -732,7 +732,7 @@ static void display_text_render(int x, int y, const char *text, int textlen, // render glyphs for (int i = 0; i < textlen; i++) { - const uint8_t *g = get_glyph(font, (uint8_t)text[i]); + const uint8_t *g = display_get_glyph(font, (uint8_t)text[i]); if (!g) continue; const uint8_t w = g[0]; // width const uint8_t h = g[1]; // height @@ -801,7 +801,7 @@ int display_text_width(const char *text, int textlen, int font) { textlen = strlen(text); } for (int i = 0; i < textlen; i++) { - const uint8_t *g = get_glyph(font, (uint8_t)text[i]); + const uint8_t *g = display_get_glyph(font, (uint8_t)text[i]); if (!g) continue; const uint8_t adv = g[2]; // advance width += adv; @@ -833,7 +833,7 @@ int display_text_split(const char *text, int textlen, int font, if (text[i] == ' ') { lastspace = i; } - const uint8_t *g = get_glyph(font, (uint8_t)text[i]); + const uint8_t *g = display_get_glyph(font, (uint8_t)text[i]); if (!g) continue; const uint8_t adv = g[2]; // advance width += adv; diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 797cc6a85f..6ea1d8e1d0 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -145,6 +145,8 @@ void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); void display_pixeldata(uint16_t c); void display_pixeldata_dirty(); +const uint8_t *display_get_glyph(int font, uint8_t c); + #if !(defined EMULATOR) && (defined TREZOR_MODEL_T) extern volatile uint8_t *const DISPLAY_CMD_ADDRESS; extern volatile uint8_t *const DISPLAY_DATA_ADDRESS; diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 7d5d59eef7..49a37162d2 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -263,6 +263,7 @@ fn generate_trezorhal_bindings() { .allowlist_function("display_pixeldata") .allowlist_function("display_pixeldata_dirty") .allowlist_function("display_set_window") + .allowlist_function("display_get_glyph") .allowlist_var("DISPLAY_CMD_ADDRESS") .allowlist_var("DISPLAY_DATA_ADDRESS") // bip39 diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index 1dcfc5bfb7..c0e9324002 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -35,6 +35,13 @@ pub fn char_width(ch: char, font: i32) -> i32 { text_width(encoding, font) } +pub fn get_char_glyph(ch: char, font: i32) -> *const u8 { + let mut buf = [0u8; 4]; + let _ = ch.encode_utf8(&mut buf); + + unsafe { ffi::display_get_glyph(font, buf[0]) } +} + pub fn text_height(font: i32) -> i32 { unsafe { ffi::display_text_height(font) } } diff --git a/core/embed/rust/src/ui/display.rs b/core/embed/rust/src/ui/display.rs index 046ca3a5bb..eb36e33669 100644 --- a/core/embed/rust/src/ui/display.rs +++ b/core/embed/rust/src/ui/display.rs @@ -3,9 +3,19 @@ use crate::{ time::Duration, trezorhal::{display, time}, }; +use core::cmp::{max, min}; use super::geometry::{Offset, Point, Rect}; +pub fn clamp_coords(pos: Point, size: Offset) -> Rect { + let x0 = max(pos.x, 0); + let y0 = max(pos.y, 0); + let x1 = min(pos.x + size.x, constant::WIDTH); + let y1 = min(pos.y + size.y, constant::HEIGHT); + + Rect::new(Point::new(x0, y0), Point::new(x1, y1)) +} + pub fn backlight() -> i32 { display::backlight(-1) } @@ -221,6 +231,111 @@ pub fn get_color_table(fg_color: Color, bg_color: Color) -> [Color; 16] { table } +pub struct Glyph { + width: i32, + height: i32, + adv: i32, + bearing_x: i32, + bearing_y: i32, + data: *const u8, +} + +impl Glyph { + pub fn new( + width: i32, + height: i32, + adv: i32, + bearing_x: i32, + bearing_y: i32, + data: *const u8, + ) -> Self { + Glyph { + width, + height, + adv, + bearing_x, + bearing_y, + data, + } + } + + pub fn print(&self, pos: Point, colortable: [Color; 16]) -> i32 { + let bearing = Offset::new(self.bearing_x as i32, -(self.bearing_y as i32)); + let size = Offset::new((self.width) as i32, (self.height) as i32); + let pos_adj = pos + bearing; + let window = clamp_coords(pos_adj, size); + + set_window(window); + + for i in window.y0..window.y1 { + for j in window.x0..window.x1 { + let rx = j - pos_adj.x; + let ry = i - pos_adj.y; + + let c = self.get_pixel_data(rx, ry); + pixeldata(colortable[c as usize]); + } + } + self.adv + } + + pub fn unpack_bpp1(&self, a: i32) -> u8 { + unsafe { + let c_data = self.data.offset((a / 8) as isize); + ((*c_data >> (7 - (a % 8))) & 0x01) * 15 + } + } + + pub fn unpack_bpp2(&self, a: i32) -> u8 { + unsafe { + let c_data = self.data.offset((a / 4) as isize); + ((*c_data >> (6 - (a % 4) * 2)) & 0x03) * 5 + } + } + + pub fn unpack_bpp4(&self, a: i32) -> u8 { + unsafe { + let c_data = self.data.offset((a / 2) as isize); + (*c_data >> (4 - (a % 2) * 4)) & 0x0F + } + } + + pub fn unpack_bpp8(&self, a: i32) -> u8 { + unsafe { + let c_data = self.data.offset((a) as isize); + *c_data >> 4 + } + } + + pub fn get_advance(&self) -> i32 { + self.adv + } + pub fn get_width(&self) -> i32 { + self.width + } + pub fn get_height(&self) -> i32 { + self.height + } + pub fn get_bearing_x(&self) -> i32 { + self.bearing_x + } + pub fn get_bearing_y(&self) -> i32 { + self.bearing_y + } + + pub fn get_pixel_data(&self, x: i32, y: i32) -> u8 { + let a = x + y * self.width; + + match constant::FONT_BPP { + 1 => self.unpack_bpp1(a), + 2 => self.unpack_bpp2(a), + 4 => self.unpack_bpp4(a), + 8 => self.unpack_bpp8(a), + _ => 0, + } + } +} + #[derive(Copy, Clone, PartialEq, Eq)] pub struct Font(i32); @@ -244,6 +359,42 @@ impl Font { pub fn line_height(self) -> i32 { constant::LINE_SPACE + self.text_height() } + + pub fn get_glyph(self, ch: char) -> Option { + let gl_data = display::get_char_glyph(ch, self.0); + + if gl_data.is_null() { + return None; + } + + unsafe { + let width = *gl_data.offset(0) as i32; + let height = *gl_data.offset(1) as i32; + let adv = *gl_data.offset(2) as i32; + let bearing_x = *gl_data.offset(3) as i32; + let bearing_y = *gl_data.offset(4) as i32; + let data = gl_data.offset(5); + Some(Glyph::new(width, height, adv, bearing_x, bearing_y, data)) + } + } + + pub fn display_text( + self, + text: &'static str, + baseline: Point, + fg_color: Color, + bg_color: Color, + ) { + let colortable = get_color_table(fg_color, bg_color); + let mut adv_total = 0; + for c in text.chars() { + let g = self.get_glyph(c); + if let Some(gly) = g { + let adv = gly.print(baseline + Offset::new(adv_total, 0), colortable); + adv_total += adv; + } + } + } } #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/core/embed/rust/src/ui/model_t1/constant.rs b/core/embed/rust/src/ui/model_t1/constant.rs index 26345617b4..d8cb1959b6 100644 --- a/core/embed/rust/src/ui/model_t1/constant.rs +++ b/core/embed/rust/src/ui/model_t1/constant.rs @@ -3,6 +3,7 @@ use crate::ui::geometry::{Offset, Point, Rect}; pub const WIDTH: i32 = 128; pub const HEIGHT: i32 = 64; pub const LINE_SPACE: i32 = 1; +pub const FONT_BPP: i32 = 1; pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT) diff --git a/core/embed/rust/src/ui/model_tr/constant.rs b/core/embed/rust/src/ui/model_tr/constant.rs index 1e01a9d84d..b41dddd8f6 100644 --- a/core/embed/rust/src/ui/model_tr/constant.rs +++ b/core/embed/rust/src/ui/model_tr/constant.rs @@ -3,6 +3,7 @@ use crate::ui::geometry::{Offset, Point, Rect}; pub const WIDTH: i32 = 128; pub const HEIGHT: i32 = 128; pub const LINE_SPACE: i32 = 1; +pub const FONT_BPP: i32 = 1; pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT) diff --git a/core/embed/rust/src/ui/model_tt/constant.rs b/core/embed/rust/src/ui/model_tt/constant.rs index 6668f4d540..86a504d112 100644 --- a/core/embed/rust/src/ui/model_tt/constant.rs +++ b/core/embed/rust/src/ui/model_tt/constant.rs @@ -3,6 +3,7 @@ use crate::ui::geometry::{Offset, Point, Rect}; pub const WIDTH: i32 = 240; pub const HEIGHT: i32 = 240; pub const LINE_SPACE: i32 = 4; +pub const FONT_BPP: i32 = 4; pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT)