Skip to content

Commit

Permalink
refactor(core): remove UTF-8 support from C
Browse files Browse the repository at this point in the history
- re-implement some fonts handling functions in Rust and delete them
from C
- C code only needs to handle ASCII characters

[no changelog]
  • Loading branch information
obrusvit committed Dec 16, 2024
1 parent 0c42254 commit 5a9c8c8
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 176 deletions.
133 changes: 28 additions & 105 deletions core/embed/gfx/fonts/fonts.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
#include <trezor_rtl.h>

#include "fonts.h"
#ifdef TRANSLATIONS
#include "librust_fonts.h"
#endif

// include selectively based on the SCons variables
#ifdef TREZOR_FONT_NORMAL_ENABLE
Expand Down Expand Up @@ -53,7 +50,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:
Expand Down Expand Up @@ -92,35 +89,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 font_info_t *font_info = get_font_info(font);
return font_info ? font_info->max_height : 0;
}

int font_baseline(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->baseline : 0;
return font_info ? font_info->glyph_nonprintable : NULL;
}

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) {
Expand All @@ -132,93 +107,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;
}

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

#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;
return font_info ? font_info->baseline : 0;
}

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

// 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;
}
20 changes: 4 additions & 16 deletions core/embed/gfx/fonts/fonts.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
#ifndef _FONTS_H
#define _FONTS_H

#include <stdbool.h>
#include <stdint.h>
#include <trezor_types.h>
#include "font_bitmap.h"

#ifdef USE_RGB_COLORS
Expand Down Expand Up @@ -51,22 +50,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
8 changes: 3 additions & 5 deletions core/embed/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 0 additions & 1 deletion core/embed/rust/librust_fonts.h

This file was deleted.

10 changes: 4 additions & 6 deletions core/embed/rust/src/translations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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()
Expand Down
37 changes: 11 additions & 26 deletions core/embed/rust/src/trezorhal/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,25 @@ 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> {
// SAFETY:
// - `ffi::get_font_info` returns either null (for invalid fonts) or a pointer
// to a static font_info_t struct
// - The font_info_t data is in ROM, making it immutable and static
// - The font_info_t contains pointers to static glyph data arrays also in ROM
// - All font data is generated at compile time and included in the binary
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.
Expand Down
Loading

0 comments on commit 5a9c8c8

Please sign in to comment.