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:
parent
e62b11b560
commit
9f98797188
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user