1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-12 17:38:13 +00:00

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
This commit is contained in:
obrusvit 2024-12-06 15:21:11 +01:00
parent e62b11b560
commit 9f98797188
7 changed files with 72 additions and 169 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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<FontInfo> {
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.

View File

@ -1,4 +1,5 @@
use crate::{
translations::get_utf8_glyph,
trezorhal::display,
ui::{
constant,
@ -129,8 +130,9 @@ impl From<Font> 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

View File

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