From 107e22c814ea09e5dbb60fc7fc65606a9b97af6c Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Fri, 3 Jun 2022 20:33:50 +0200 Subject: [PATCH] feat(core/rust): glyph and text rendering in rust --- core/embed/extmod/modtrezorui/display.c | 8 +- core/embed/extmod/modtrezorui/display.h | 2 + core/embed/rust/build.rs | 1 + core/embed/rust/src/trezorhal/display.rs | 4 + core/embed/rust/src/ui/display.rs | 120 ++++++++++++++++++++ core/embed/rust/src/ui/geometry.rs | 27 ++++- core/embed/rust/src/ui/model_t1/constant.rs | 1 + core/embed/rust/src/ui/model_tr/constant.rs | 1 + core/embed/rust/src/ui/model_tt/constant.rs | 1 + 9 files changed, 160 insertions(+), 5 deletions(-) diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index a377b0de3..d6fc8c236 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 dcad222e3..7a9455c14 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -144,6 +144,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 efd42ed7e..03bc3fa59 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -268,6 +268,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 8329e0ffe..145c3184a 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -36,6 +36,10 @@ pub fn char_width(ch: char, font: i32) -> i32 { text_width(encoding, font) } +pub fn get_char_glyph(ch: u8, font: i32) -> *const u8 { + unsafe { ffi::display_get_glyph(font, ch) } +} + 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 b372f136c..d6cb41569 100644 --- a/core/embed/rust/src/ui/display.rs +++ b/core/embed/rust/src/ui/display.rs @@ -5,6 +5,7 @@ use crate::{ trezorhal::{display, qr, time}, ui::lerp::Lerp, }; +use core::slice; use super::geometry::{Offset, Point, Rect}; @@ -253,6 +254,104 @@ pub fn get_color_table(fg_color: Color, bg_color: Color) -> [Color; 16] { table } +pub struct Glyph { + pub width: i32, + pub height: i32, + pub adv: i32, + pub bearing_x: i32, + pub bearing_y: i32, + data: &'static [u8], +} + +impl Glyph { + /// Construct a `Glyph` from a raw pointer. + /// + /// # Safety + /// + /// This function is unsafe because the caller has to guarantee that `data` + /// is pointing to a memory containing a valid glyph data, that is: + /// - contains valid glyph metadata + /// - data has appropriate size + /// - data must have static lifetime + pub unsafe fn load(data: *const u8) -> Self { + unsafe { + let width = *data.offset(0) as i32; + let height = *data.offset(1) as i32; + + let data_bits = constant::FONT_BPP * width * height; + + let data_bytes = if data_bits % 8 == 0 { + data_bits / 8 + } else { + (data_bits / 8) + 1 + }; + + Glyph { + width, + height, + adv: *data.offset(2) as i32, + bearing_x: *data.offset(3) as i32, + bearing_y: *data.offset(4) as i32, + data: slice::from_raw_parts(data.offset(5), data_bytes as usize), + } + } + } + + pub fn print(&self, pos: Point, colortable: [Color; 16]) -> i32 { + let bearing = Offset::new(self.bearing_x, -self.bearing_y); + let size = Offset::new(self.width, self.height); + let pos_adj = pos + bearing; + let r = Rect::from_top_left_and_size(pos_adj, size); + + let area = r.translate(get_offset()); + let window = area.clamp(constant::screen()); + + set_window(window); + + for y in window.y0..window.y1 { + for x in window.x0..window.x1 { + let p = Point::new(x, y); + let r = p - pos_adj; + let c = self.get_pixel_data(r); + pixeldata(colortable[c as usize]); + } + } + self.adv + } + + pub fn unpack_bpp1(&self, a: i32) -> u8 { + let c_data = self.data[(a / 8) as usize]; + ((c_data >> (7 - (a % 8))) & 0x01) * 15 + } + + pub fn unpack_bpp2(&self, a: i32) -> u8 { + let c_data = self.data[(a / 4) as usize]; + ((c_data >> (6 - (a % 4) * 2)) & 0x03) * 5 + } + + pub fn unpack_bpp4(&self, a: i32) -> u8 { + let c_data = self.data[(a / 2) as usize]; + (c_data >> (4 - (a % 2) * 4)) & 0x0F + } + + pub fn unpack_bpp8(&self, a: i32) -> u8 { + let c_data = self.data[a as usize]; + c_data >> 4 + } + + pub fn get_pixel_data(&self, p: Offset) -> u8 { + let a = p.x + p.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); @@ -276,6 +375,27 @@ impl Font { pub fn line_height(self) -> i32 { constant::LINE_SPACE + self.text_height() } + + pub fn get_glyph(self, char_byte: u8) -> Option { + let gl_data = display::get_char_glyph(char_byte, self.0); + + if gl_data.is_null() { + return None; + } + unsafe { Some(Glyph::load(gl_data)) } + } + + pub fn display_text(self, text: &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.bytes() { + 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/geometry.rs b/core/embed/rust/src/ui/geometry.rs index 8d74d2699..af68fb6cf 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -297,7 +297,32 @@ impl Rect { self.split_left(self.width() - width) } - pub fn translate(&self, offset: Offset) -> Self { + const fn _max(a: i32, b: i32) -> i32 { + if a > b { + a + } else { + b + } + } + + const fn _min(a: i32, b: i32) -> i32 { + if a < b { + a + } else { + b + } + } + + pub const fn clamp(self, limit: Rect) -> Self { + Self { + x0: Rect::_max(self.x0, limit.x0), + y0: Rect::_max(self.y0, limit.y0), + x1: Rect::_min(self.x1, limit.x1), + y1: Rect::_min(self.y1, limit.y1), + } + } + + pub const fn translate(&self, offset: Offset) -> Self { Self { x0: self.x0 + offset.x, y0: self.y0 + offset.y, diff --git a/core/embed/rust/src/ui/model_t1/constant.rs b/core/embed/rust/src/ui/model_t1/constant.rs index 26345617b..d8cb1959b 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 1e01a9d84..b41dddd8f 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 6668f4d54..86a504d11 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)