mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-19 14:08:11 +00:00
chore(core/rust): move Font and Color into their own modules
This commit is contained in:
parent
5940129bfc
commit
1bb6288512
99
core/embed/rust/src/ui/display/color.rs
Normal file
99
core/embed/rust/src/ui/display/color.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use crate::ui::lerp::Lerp;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Color(u16);
|
||||
|
||||
impl Color {
|
||||
pub const fn from_u16(val: u16) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r = (r as u16 & 0xF8) << 8;
|
||||
let g = (g as u16 & 0xFC) << 3;
|
||||
let b = (b as u16 & 0xF8) >> 3;
|
||||
Self(r | g | b)
|
||||
}
|
||||
|
||||
pub const fn luminance(self) -> u32 {
|
||||
(self.r() as u32 * 299) / 1000
|
||||
+ (self.g() as u32 * 587) / 1000
|
||||
+ (self.b() as u32 * 114) / 1000
|
||||
}
|
||||
|
||||
pub const fn rgba(bg: Color, r: u8, g: u8, b: u8, alpha: u16) -> Self {
|
||||
let r_u16 = r as u16;
|
||||
let g_u16 = g as u16;
|
||||
let b_u16 = b as u16;
|
||||
|
||||
let r = ((256 - alpha) * bg.r() as u16 + alpha * r_u16) >> 8;
|
||||
let g = ((256 - alpha) * bg.g() as u16 + alpha * g_u16) >> 8;
|
||||
let b = ((256 - alpha) * bg.b() as u16 + alpha * b_u16) >> 8;
|
||||
|
||||
let r = (r & 0xF8) << 8;
|
||||
let g = (g & 0xFC) << 3;
|
||||
let b = (b & 0xF8) >> 3;
|
||||
Self(r | g | b)
|
||||
}
|
||||
|
||||
pub const fn alpha(bg: Color, alpha: u16) -> Self {
|
||||
Self::rgba(bg, 0xFF, 0xFF, 0xFF, alpha)
|
||||
}
|
||||
|
||||
pub const fn r(self) -> u8 {
|
||||
(self.0 >> 8) as u8 & 0xF8
|
||||
}
|
||||
|
||||
pub const fn g(self) -> u8 {
|
||||
(self.0 >> 3) as u8 & 0xFC
|
||||
}
|
||||
|
||||
pub const fn b(self) -> u8 {
|
||||
(self.0 << 3) as u8 & 0xF8
|
||||
}
|
||||
|
||||
pub fn to_u16(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn hi_byte(self) -> u8 {
|
||||
(self.to_u16() >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn lo_byte(self) -> u8 {
|
||||
(self.to_u16() & 0xFF) as u8
|
||||
}
|
||||
|
||||
pub fn negate(self) -> Self {
|
||||
Self(!self.0)
|
||||
}
|
||||
|
||||
pub const fn white() -> Self {
|
||||
Self::rgb(255, 255, 255)
|
||||
}
|
||||
|
||||
pub const fn black() -> Self {
|
||||
Self::rgb(0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lerp for Color {
|
||||
fn lerp(a: Self, b: Self, t: f32) -> Self {
|
||||
let r = u8::lerp(a.r(), b.r(), t);
|
||||
let g = u8::lerp(a.g(), b.g(), t);
|
||||
let b = u8::lerp(a.b(), b.b(), t);
|
||||
Color::rgb(r, g, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Color {
|
||||
fn from(val: u16) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for u16 {
|
||||
fn from(val: Color) -> Self {
|
||||
val.to_u16()
|
||||
}
|
||||
}
|
213
core/embed/rust/src/ui/display/font.rs
Normal file
213
core/embed/rust/src/ui/display/font.rs
Normal file
@ -0,0 +1,213 @@
|
||||
use crate::{
|
||||
trezorhal::display,
|
||||
ui::{
|
||||
constant,
|
||||
geometry::{Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
use core::slice;
|
||||
|
||||
use super::{get_color_table, get_offset, pixeldata, set_window, Color};
|
||||
|
||||
pub struct Glyph {
|
||||
pub width: i16,
|
||||
pub height: i16,
|
||||
pub adv: i16,
|
||||
pub bearing_x: i16,
|
||||
pub bearing_y: i16,
|
||||
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 i16;
|
||||
let height = *data.offset(1) as i16;
|
||||
|
||||
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 i16,
|
||||
bearing_x: *data.offset(3) as i16,
|
||||
bearing_y: *data.offset(4) as i16,
|
||||
data: slice::from_raw_parts(data.offset(5), data_bytes as usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self, pos: Point, colortable: [Color; 16]) -> i16 {
|
||||
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: i16) -> u8 {
|
||||
let c_data = self.data[(a / 8) as usize];
|
||||
((c_data >> (7 - (a % 8))) & 0x01) * 15
|
||||
}
|
||||
|
||||
pub fn unpack_bpp2(&self, a: i16) -> u8 {
|
||||
let c_data = self.data[(a / 4) as usize];
|
||||
((c_data >> (6 - (a % 4) * 2)) & 0x03) * 5
|
||||
}
|
||||
|
||||
pub fn unpack_bpp4(&self, a: i16) -> u8 {
|
||||
let c_data = self.data[(a / 2) as usize];
|
||||
(c_data >> (4 - (a % 2) * 4)) & 0x0F
|
||||
}
|
||||
|
||||
pub fn unpack_bpp8(&self, a: i16) -> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Font constants. Keep in sync with FONT_ definitions in
|
||||
/// `extmod/modtrezorui/fonts/fonts.h`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Font {
|
||||
NORMAL = 1,
|
||||
BOLD = 2,
|
||||
MONO = 3,
|
||||
DEMIBOLD = 5,
|
||||
}
|
||||
|
||||
impl From<Font> for i32 {
|
||||
fn from(font: Font) -> i32 {
|
||||
-(font as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn text_width(self, text: &str) -> i16 {
|
||||
display::text_width(text, self.into())
|
||||
}
|
||||
|
||||
pub fn char_width(self, ch: char) -> i16 {
|
||||
display::char_width(ch, self.into())
|
||||
}
|
||||
|
||||
pub fn text_height(self) -> i16 {
|
||||
display::text_height(self.into())
|
||||
}
|
||||
|
||||
pub fn text_max_height(self) -> i16 {
|
||||
display::text_max_height(self.into())
|
||||
}
|
||||
|
||||
pub fn text_baseline(self) -> i16 {
|
||||
display::text_baseline(self.into())
|
||||
}
|
||||
|
||||
pub fn max_height(self) -> i16 {
|
||||
display::text_max_height(self.into())
|
||||
}
|
||||
|
||||
pub fn line_height(self) -> i16 {
|
||||
constant::LINE_SPACE + self.text_height()
|
||||
}
|
||||
|
||||
pub fn get_glyph(self, char_byte: u8) -> Option<Glyph> {
|
||||
let gl_data = display::get_char_glyph(char_byte, self.into());
|
||||
|
||||
if gl_data.is_null() {
|
||||
return None;
|
||||
}
|
||||
// SAFETY: Glyph::load is valid for data returned by get_char_glyph
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the length of the longest suffix from a given `text`
|
||||
/// that will fit into the area `width` pixels wide.
|
||||
pub fn longest_suffix(self, width: i16, text: &str) -> usize {
|
||||
let mut text_width = 0;
|
||||
for (chars_from_right, c) in text.chars().rev().enumerate() {
|
||||
let c_width = self.char_width(c);
|
||||
if text_width + c_width > width {
|
||||
// Another character cannot be fitted, we're done.
|
||||
return chars_from_right;
|
||||
}
|
||||
text_width += c_width;
|
||||
}
|
||||
|
||||
text.len() // it fits in its entirety
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GlyphMetrics {
|
||||
fn char_width(&self, ch: char) -> i16;
|
||||
fn text_width(&self, text: &str) -> i16;
|
||||
fn line_height(&self) -> i16;
|
||||
}
|
||||
|
||||
impl GlyphMetrics for Font {
|
||||
fn char_width(&self, ch: char) -> i16 {
|
||||
Font::char_width(*self, ch)
|
||||
}
|
||||
|
||||
fn text_width(&self, text: &str) -> i16 {
|
||||
Font::text_width(*self, text)
|
||||
}
|
||||
|
||||
fn line_height(&self) -> i16 {
|
||||
Font::line_height(*self)
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
pub mod color;
|
||||
pub mod font;
|
||||
pub mod loader;
|
||||
#[cfg(feature = "jpeg")]
|
||||
pub mod tjpgd;
|
||||
@ -919,290 +921,3 @@ pub fn get_color_table(fg_color: Color, bg_color: Color) -> [Color; 16] {
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
pub struct Glyph {
|
||||
pub width: i16,
|
||||
pub height: i16,
|
||||
pub adv: i16,
|
||||
pub bearing_x: i16,
|
||||
pub bearing_y: i16,
|
||||
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 i16;
|
||||
let height = *data.offset(1) as i16;
|
||||
|
||||
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 i16,
|
||||
bearing_x: *data.offset(3) as i16,
|
||||
bearing_y: *data.offset(4) as i16,
|
||||
data: slice::from_raw_parts(data.offset(5), data_bytes as usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self, pos: Point, colortable: [Color; 16]) -> i16 {
|
||||
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: i16) -> u8 {
|
||||
let c_data = self.data[(a / 8) as usize];
|
||||
((c_data >> (7 - (a % 8))) & 0x01) * 15
|
||||
}
|
||||
|
||||
pub fn unpack_bpp2(&self, a: i16) -> u8 {
|
||||
let c_data = self.data[(a / 4) as usize];
|
||||
((c_data >> (6 - (a % 4) * 2)) & 0x03) * 5
|
||||
}
|
||||
|
||||
pub fn unpack_bpp4(&self, a: i16) -> u8 {
|
||||
let c_data = self.data[(a / 2) as usize];
|
||||
(c_data >> (4 - (a % 2) * 4)) & 0x0F
|
||||
}
|
||||
|
||||
pub fn unpack_bpp8(&self, a: i16) -> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Font constants. Keep in sync with FONT_ definitions in
|
||||
/// `extmod/modtrezorui/fonts/fonts.h`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Font {
|
||||
NORMAL = 1,
|
||||
BOLD = 2,
|
||||
MONO = 3,
|
||||
DEMIBOLD = 5,
|
||||
}
|
||||
|
||||
impl From<Font> for i32 {
|
||||
fn from(font: Font) -> i32 {
|
||||
-(font as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub fn text_width(self, text: &str) -> i16 {
|
||||
display::text_width(text, self.into())
|
||||
}
|
||||
|
||||
pub fn char_width(self, ch: char) -> i16 {
|
||||
display::char_width(ch, self.into())
|
||||
}
|
||||
|
||||
pub fn text_height(self) -> i16 {
|
||||
display::text_height(self.into())
|
||||
}
|
||||
|
||||
pub fn text_max_height(self) -> i16 {
|
||||
display::text_max_height(self.into())
|
||||
}
|
||||
|
||||
pub fn text_baseline(self) -> i16 {
|
||||
display::text_baseline(self.into())
|
||||
}
|
||||
|
||||
pub fn max_height(self) -> i16 {
|
||||
display::text_max_height(self.into())
|
||||
}
|
||||
|
||||
pub fn baseline(self) -> i16 {
|
||||
display::text_baseline(self.into())
|
||||
}
|
||||
|
||||
pub fn line_height(self) -> i16 {
|
||||
constant::LINE_SPACE + self.text_height()
|
||||
}
|
||||
|
||||
pub fn get_glyph(self, char_byte: u8) -> Option<Glyph> {
|
||||
let gl_data = display::get_char_glyph(char_byte, self.into());
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the length of the longest suffix from a given `text`
|
||||
/// that will fit into the area `width` pixels wide.
|
||||
pub fn longest_suffix(self, width: i16, text: &str) -> usize {
|
||||
let mut text_width = 0;
|
||||
for (chars_from_right, c) in text.chars().rev().enumerate() {
|
||||
let c_width = self.char_width(c);
|
||||
if text_width + c_width > width {
|
||||
// Another character cannot be fitted, we're done.
|
||||
return chars_from_right;
|
||||
}
|
||||
text_width += c_width;
|
||||
}
|
||||
|
||||
text.len() // it fits in its entirety
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Color(u16);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alpha {
|
||||
($n: expr) => {
|
||||
if ($n >= 1.0) {
|
||||
256_u16
|
||||
} else {
|
||||
($n * 256.0) as u16
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const fn from_u16(val: u16) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r = (r as u16 & 0xF8) << 8;
|
||||
let g = (g as u16 & 0xFC) << 3;
|
||||
let b = (b as u16 & 0xF8) >> 3;
|
||||
Self(r | g | b)
|
||||
}
|
||||
|
||||
pub const fn luminance(self) -> u32 {
|
||||
(self.r() as u32 * 299) / 1000
|
||||
+ (self.g() as u32 * 587) / 1000
|
||||
+ (self.b() as u32 * 114) / 1000
|
||||
}
|
||||
|
||||
pub const fn rgba(bg: Color, r: u8, g: u8, b: u8, alpha: u16) -> Self {
|
||||
let r_u16 = r as u16;
|
||||
let g_u16 = g as u16;
|
||||
let b_u16 = b as u16;
|
||||
|
||||
let r = ((256 - alpha) * bg.r() as u16 + alpha * r_u16) >> 8;
|
||||
let g = ((256 - alpha) * bg.g() as u16 + alpha * g_u16) >> 8;
|
||||
let b = ((256 - alpha) * bg.b() as u16 + alpha * b_u16) >> 8;
|
||||
|
||||
let r = (r & 0xF8) << 8;
|
||||
let g = (g & 0xFC) << 3;
|
||||
let b = (b & 0xF8) >> 3;
|
||||
Self(r | g | b)
|
||||
}
|
||||
|
||||
pub const fn alpha(bg: Color, alpha: u16) -> Self {
|
||||
Self::rgba(bg, 0xFF, 0xFF, 0xFF, alpha)
|
||||
}
|
||||
|
||||
pub const fn r(self) -> u8 {
|
||||
(self.0 >> 8) as u8 & 0xF8
|
||||
}
|
||||
|
||||
pub const fn g(self) -> u8 {
|
||||
(self.0 >> 3) as u8 & 0xFC
|
||||
}
|
||||
|
||||
pub const fn b(self) -> u8 {
|
||||
(self.0 << 3) as u8 & 0xF8
|
||||
}
|
||||
|
||||
pub fn to_u16(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn hi_byte(self) -> u8 {
|
||||
(self.to_u16() >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn lo_byte(self) -> u8 {
|
||||
(self.to_u16() & 0xFF) as u8
|
||||
}
|
||||
|
||||
pub fn negate(self) -> Self {
|
||||
Self(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lerp for Color {
|
||||
fn lerp(a: Self, b: Self, t: f32) -> Self {
|
||||
let r = u8::lerp(a.r(), b.r(), t);
|
||||
let g = u8::lerp(a.g(), b.g(), t);
|
||||
let b = u8::lerp(a.b(), b.b(), t);
|
||||
Color::rgb(r, g, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Color {
|
||||
fn from(val: u16) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for u16 {
|
||||
fn from(val: Color) -> Self {
|
||||
val.to_u16()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user