From 9f987971882f51f7b3a3afc4675d7058b7a84dc7 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Fri, 6 Dec 2024 15:21:11 +0100 Subject: [PATCH] refactor(core): remove UTF-8 support from C - re-implement some fonts handling functions in Rust and delete them from C - C code only needs to handle ASCII characters --- core/embed/gfx/fonts/fonts.c | 130 +++++------------------ core/embed/gfx/fonts/fonts.h | 17 +-- core/embed/rust/build.rs | 8 +- core/embed/rust/src/translations/mod.rs | 10 +- core/embed/rust/src/trezorhal/display.rs | 31 +----- core/embed/rust/src/ui/display/font.rs | 43 +++++--- core/embed/rust/src/ui/display/mod.rs | 2 +- 7 files changed, 72 insertions(+), 169 deletions(-) diff --git a/core/embed/gfx/fonts/fonts.c b/core/embed/gfx/fonts/fonts.c index d0484dfe76..b2b5b67021 100644 --- a/core/embed/gfx/fonts/fonts.c +++ b/core/embed/gfx/fonts/fonts.c @@ -53,7 +53,7 @@ #define PASTER(font_name) font_name##_info #define FONT_INFO(font_name) PASTER(font_name) -static const font_info_t *get_font_info(font_id_t font_id) { +const font_info_t *get_font_info(font_id_t font_id) { switch (font_id) { #ifdef TREZOR_FONT_NORMAL_ENABLE case FONT_NORMAL: @@ -92,35 +92,13 @@ static const font_info_t *get_font_info(font_id_t font_id) { } } -int font_height(font_id_t font_id) { - const font_info_t *font_info = get_font_info(font_id); - return font_info ? font_info->height : 0; -} - -int font_max_height(font_id_t font) { +const uint8_t *font_nonprintable_glyph(font_id_t font) { const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->max_height : 0; + return font_info ? font_info->glyph_nonprintable : NULL; } -int font_baseline(font_id_t font) { - const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->baseline : 0; -} - -const uint8_t *font_get_glyph(font_id_t font, uint16_t c) { -#ifdef TRANSLATIONS - // found UTF8 character - // it is not hardcoded in firmware fonts, it must be extracted from the - // embedded blob - if (c >= 0x7F) { - const uint8_t *g = get_utf8_glyph(c, font); - if (g != NULL) { - return g; - } - } -#endif - - // printable ASCII character +const uint8_t *font_get_glyph(font_id_t font, char c) { + // support only printable ASCII character if (c >= ' ' && c < 0x7F) { const font_info_t *font_info = get_font_info(font); if (font_info == NULL) { @@ -132,93 +110,41 @@ const uint8_t *font_get_glyph(font_id_t font, uint16_t c) { return font_nonprintable_glyph(font); } -const uint8_t *font_nonprintable_glyph(font_id_t font) { +int font_baseline(font_id_t font) { const font_info_t *font_info = get_font_info(font); - return font_info ? font_info->glyph_nonprintable : NULL; + return font_info ? font_info->baseline : 0; } -font_glyph_iter_t font_glyph_iter_init(font_id_t font, const uint8_t *text, - const int len) { - return (font_glyph_iter_t){ - .font = font, - .text = text, - .remaining = len, - }; +int font_max_height(font_id_t font) { + const font_info_t *font_info = get_font_info(font); + return font_info ? font_info->max_height : 0; } -#define UNICODE_BADCHAR 0xFFFD -#define IS_UTF8_CONTINUE(c) (((c) & 0b11000000) == 0b10000000) - -static uint16_t next_utf8_codepoint(font_glyph_iter_t *iter) { - uint16_t out; - assert(iter->remaining > 0); - // 1-byte UTF-8 character - if (iter->text[0] < 0x7f) { - out = iter->text[0]; - ++iter->text; - --iter->remaining; - return out; - } - // 2-byte UTF-8 character - if (iter->remaining >= 2 && ((iter->text[0] & 0b11100000) == 0b11000000) && - IS_UTF8_CONTINUE(iter->text[1])) { - out = (((uint16_t)iter->text[0] & 0b00011111) << 6) | - (iter->text[1] & 0b00111111); - iter->text += 2; - iter->remaining -= 2; - return out; - } - // 3-byte UTF-8 character - if (iter->remaining >= 3 && ((iter->text[0] & 0b11110000) == 0b11100000) && - IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2])) { - out = (((uint16_t)iter->text[0] & 0b00001111) << 12) | - (((uint16_t)iter->text[1] & 0b00111111) << 6) | - (iter->text[2] & 0b00111111); - iter->text += 3; - iter->remaining -= 3; - return out; - } - // 4-byte UTF-8 character - if (iter->remaining >= 4 && ((iter->text[0] & 0b11111000) == 0b11110000) && - IS_UTF8_CONTINUE(iter->text[1]) && IS_UTF8_CONTINUE(iter->text[2]) && - IS_UTF8_CONTINUE(iter->text[3])) { - // we use 16-bit codepoints, so we can't represent 4-byte UTF-8 characters - iter->text += 4; - iter->remaining -= 4; - return UNICODE_BADCHAR; - } - - ++iter->text; - --iter->remaining; - return UNICODE_BADCHAR; -} - -bool font_next_glyph(font_glyph_iter_t *iter, const uint8_t **out) { - if (iter->remaining <= 0) { - return false; - } - uint16_t c = next_utf8_codepoint(iter); - *out = font_get_glyph(iter->font, c); - if (*out == NULL) { - // should not happen but ¯\_(ツ)_/¯ - return font_next_glyph(iter, out); - } else { - return true; - } -} - -// compute the width of the text (in pixels) +// compute the width of the ASCII text (in pixels) int font_text_width(font_id_t font, const char *text, int textlen) { int width = 0; // determine text length if not provided if (textlen < 0) { textlen = strlen(text); } - font_glyph_iter_t iter = font_glyph_iter_init(font, (uint8_t *)text, textlen); - const uint8_t *g = NULL; - while (font_next_glyph(&iter, &g)) { - const uint8_t adv = g[2]; // advance + + const font_info_t *font_info = get_font_info(font); + if (font_info == NULL) { + return 0; + } + + for (int i = 0; i < textlen; ++i) { + const uint8_t *glyph; + char c = text[i]; + + if (c >= ' ' && c < 0x7F) { + glyph = font_info->glyph_data[c - ' ']; + } else { + glyph = font_info->glyph_nonprintable; + } + const uint8_t adv = glyph[2]; // advance width += adv; } + return width; } diff --git a/core/embed/gfx/fonts/fonts.h b/core/embed/gfx/fonts/fonts.h index dd95ebe22f..9fc1919764 100644 --- a/core/embed/gfx/fonts/fonts.h +++ b/core/embed/gfx/fonts/fonts.h @@ -51,22 +51,11 @@ typedef enum { FONT_SUB = -8, } font_id_t; -/// Font glyph iterator structure -typedef struct { - const font_id_t font; - const uint8_t *text; - int remaining; -} font_glyph_iter_t; +const font_info_t *get_font_info(font_id_t font_id); +const uint8_t *font_get_glyph(font_id_t font, const char c); -int font_height(font_id_t font); -int font_max_height(font_id_t font); int font_baseline(font_id_t font); -const uint8_t *font_get_glyph(font_id_t font, const uint16_t c); -const uint8_t *font_nonprintable_glyph(font_id_t font); - -font_glyph_iter_t font_glyph_iter_init(font_id_t font, const uint8_t *text, - const int len); -bool font_next_glyph(font_glyph_iter_t *iter, const uint8_t **out); +int font_max_height(font_id_t font); int font_text_width(font_id_t font, const char *text, int textlen); #endif //_FONTS_H diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 4a9e1da114..83e4022894 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -379,11 +379,9 @@ fn generate_trezorhal_bindings() { .allowlist_function("gfx_mono8_blend_mono4") .allowlist_function("gfx_bitblt_wait") // fonts - .allowlist_function("font_height") - .allowlist_function("font_max_height") - .allowlist_function("font_baseline") - .allowlist_function("font_get_glyph") - .allowlist_function("font_text_width") + .allowlist_type("font_info_t") + .allowlist_function("get_font_info") + // .allowlist_function("font_get_glyph") // uzlib .allowlist_function("uzlib_uncompress_init") .allowlist_function("uzlib_uncompress") diff --git a/core/embed/rust/src/translations/mod.rs b/core/embed/rust/src/translations/mod.rs index 3d19ef4b48..17b9544cd7 100644 --- a/core/embed/rust/src/translations/mod.rs +++ b/core/embed/rust/src/translations/mod.rs @@ -7,17 +7,15 @@ mod public_keys; mod translated_string; pub use translated_string::TranslatedString as TR; + +use crate::ui::display::Font; pub const DEFAULT_LANGUAGE: &str = "en-US"; /// # Safety /// /// Returned pointer will only point to valid font data for as long as /// the flash content is not invalidated by `erase()` or `write()`. -#[no_mangle] -pub unsafe extern "C" fn get_utf8_glyph(codepoint: cty::uint16_t, font: cty::c_int) -> *const u8 { - // C will send a negative number - let font_abs = font.unsigned_abs() as u16; - +pub unsafe fn get_utf8_glyph(codepoint: u16, font: Font) -> *const u8 { // SAFETY: Reference is discarded at the end of the function. // We do return a _pointer_ to the same memory location, but the pointer is // always valid. @@ -27,7 +25,7 @@ pub unsafe extern "C" fn get_utf8_glyph(codepoint: cty::uint16_t, font: cty::c_i let Some(tr) = translations.as_ref() else { return core::ptr::null(); }; - if let Some(glyph) = tr.font(font_abs).and_then(|t| t.get(codepoint)) { + if let Some(glyph) = tr.font(font as u16).and_then(|t| t.get(codepoint)) { glyph.as_ptr() } else { core::ptr::null() diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index c614cb5556..99d430fe4f 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -5,40 +5,19 @@ use core::ptr; pub use ffi::{DISPLAY_RESX, DISPLAY_RESY}; +pub type FontInfo = ffi::font_info_t; + pub fn backlight(val: i32) -> i32 { unsafe { ffi::display_set_backlight(val) } } -pub fn text_width(text: &str, font: i32) -> i16 { +pub fn get_font_info(font: i32) -> Option { unsafe { - ffi::font_text_width(font, text.as_ptr() as _, text.len() as _) - .try_into() - .unwrap_or(i16::MAX) + let font = ffi::get_font_info(font); + Some(*font.as_ref()?) } } -pub fn char_width(ch: char, font: i32) -> i16 { - let mut buf = [0u8; 4]; - let encoding = ch.encode_utf8(&mut buf); - text_width(encoding, font) -} - -pub fn get_char_glyph(ch: u16, font: i32) -> *const u8 { - unsafe { ffi::font_get_glyph(font, ch) } -} - -pub fn text_height(font: i32) -> i16 { - unsafe { ffi::font_height(font).try_into().unwrap_or(i16::MAX) } -} - -pub fn text_max_height(font: i32) -> i16 { - unsafe { ffi::font_max_height(font).try_into().unwrap_or(i16::MAX) } -} - -pub fn text_baseline(font: i32) -> i16 { - unsafe { ffi::font_baseline(font).try_into().unwrap_or(i16::MAX) } -} - pub fn sync() { // NOTE: The sync operation is not called for tests because the linker // would otherwise report missing symbols if the tests are built with ASAN. diff --git a/core/embed/rust/src/ui/display/font.rs b/core/embed/rust/src/ui/display/font.rs index f126c85301..3c1f6ef37a 100644 --- a/core/embed/rust/src/ui/display/font.rs +++ b/core/embed/rust/src/ui/display/font.rs @@ -1,4 +1,5 @@ use crate::{ + translations::get_utf8_glyph, trezorhal::display, ui::{ constant, @@ -129,8 +130,9 @@ impl From for i32 { } impl Font { + /// Supports UTF8 characters pub fn text_width(self, text: &str) -> i16 { - display::text_width(text, self.into()) + text.chars().fold(0, |acc, c| acc + self.get_glyph(c).adv) } /// Supports UTF8 characters @@ -198,31 +200,28 @@ impl Font { return 0; } - if let Some(glyph) = self.get_first_glyph_from_text(text) { - glyph.bearing_x - } else { - 0 - } + self.get_first_glyph_from_text(text) + .map_or(0, |glyph| glyph.bearing_x) } pub fn char_width(self, ch: char) -> i16 { - display::char_width(ch, self.into()) + let mut buf = [0u8; 4]; + let encoding = ch.encode_utf8(&mut buf); + self.text_width(encoding) } pub fn text_height(self) -> i16 { - display::text_height(self.into()) + display::get_font_info(self.into()).map_or(i16::MAX, |font| font.height.try_into().unwrap()) } pub fn text_max_height(self) -> i16 { - display::text_max_height(self.into()) + display::get_font_info(self.into()) + .map_or(i16::MAX, |font| font.max_height.try_into().unwrap()) } pub fn text_baseline(self) -> i16 { - display::text_baseline(self.into()) - } - - pub fn max_height(self) -> i16 { - display::text_max_height(self.into()) + display::get_font_info(self.into()) + .map_or(i16::MAX, |font| font.baseline.try_into().unwrap()) } pub fn line_height(self) -> i16 { @@ -248,6 +247,20 @@ impl Font { (start + end + self.visible_text_height(text)) / 2 } + fn get_glyph_data(&self, c: u16) -> *const u8 { + display::get_font_info((*self).into()).map_or(core::ptr::null(), |font_info| { + if c >= 0x7F { + // UTF8 character from embedded blob + unsafe { get_utf8_glyph(c, *self) } + } else if c >= ' ' as u16 && c < 0x7F { + // ASCII character + unsafe { *font_info.glyph_data.offset((c - ' ' as u16) as isize) } + } else { + font_info.glyph_nonprintable + } + }) + } + pub fn get_glyph(self, ch: char) -> Glyph { /* have the non-breaking space counted for width but not counted as a * breaking point */ @@ -255,7 +268,7 @@ impl Font { '\u{00a0}' => '\u{0020}', c => c, }; - let gl_data = display::get_char_glyph(ch as u16, self.into()); + let gl_data = self.get_glyph_data(ch as u16); ensure!(!gl_data.is_null(), "Failed to load glyph"); // SAFETY: Glyph::load is valid for data returned by get_char_glyph diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index f2290fd316..4a8d3c9509 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -77,7 +77,7 @@ impl TextOverlay { area, text: text.into(), font, - max_height: font.max_height(), + max_height: font.text_max_height(), baseline: font.text_baseline(), } }