diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 50e179005..e40735ce4 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -289,7 +289,6 @@ fn generate_trezorhal_bindings() { .allowlist_function("display_bar_radius") .allowlist_function("display_bar_radius_buffer") .allowlist_function("display_image") - .allowlist_function("display_toif_info") .allowlist_function("display_loader") .allowlist_function("display_pixeldata") .allowlist_function("display_pixeldata_dirty") diff --git a/core/embed/rust/src/trezorhal/display.rs b/core/embed/rust/src/trezorhal/display.rs index ca50482f7..32083210e 100644 --- a/core/embed/rust/src/trezorhal/display.rs +++ b/core/embed/rust/src/trezorhal/display.rs @@ -1,7 +1,6 @@ use super::ffi; use core::ptr; use cty::c_int; -use num_traits::FromPrimitive; use crate::trezorhal::buffers::BufferText; @@ -13,12 +12,6 @@ pub enum ToifFormat { GrayScaleEH = ffi::toif_format_t_TOIF_GRAYSCALE_EH as _, } -pub struct ToifInfo { - pub width: u16, - pub height: u16, - pub format: ToifFormat, -} - pub fn backlight(val: i32) -> i32 { unsafe { ffi::display_backlight(val) } } @@ -116,31 +109,6 @@ pub fn image(x: i16, y: i16, w: i16, h: i16, data: &[u8]) { } } -pub fn toif_info(data: &[u8]) -> Result { - let mut width: cty::uint16_t = 0; - let mut height: cty::uint16_t = 0; - let mut format: ffi::toif_format_t = ffi::toif_format_t_TOIF_FULL_COLOR_BE; - if unsafe { - ffi::display_toif_info( - data.as_ptr() as _, - data.len() as _, - &mut width, - &mut height, - &mut format, - ) - } { - let format = ToifFormat::from_usize(format as usize).unwrap_or(ToifFormat::FullColorBE); - - Ok(ToifInfo { - width, - height, - format, - }) - } else { - Err(()) - } -} - pub fn loader( progress: u16, indeterminate: bool, diff --git a/core/embed/rust/src/ui/component/image.rs b/core/embed/rust/src/ui/component/image.rs index 64c095406..4f18b7270 100644 --- a/core/embed/rust/src/ui/component/image.rs +++ b/core/embed/rust/src/ui/component/image.rs @@ -16,8 +16,8 @@ pub struct Image { impl Image { pub fn new(data: &'static [u8]) -> Self { - let toif = Toif::new(data); - assert!(toif.format == ToifFormat::FullColorLE); + let toif = unwrap!(Toif::new(data)); + assert!(toif.format() == ToifFormat::FullColorLE); Self { toif, area: Rect::zero(), @@ -27,8 +27,8 @@ impl Image { /// Display the icon with baseline Point, aligned according to the /// `alignment` argument. pub fn draw(&self, baseline: Point, alignment: Alignment2D) { - let r = Rect::snap(baseline, self.toif.size, alignment); - image(r.x0, r.y0, r.width(), r.height(), self.toif.data); + let r = Rect::snap(baseline, self.toif.size(), alignment); + image(r.x0, r.y0, r.width(), r.height(), self.toif.zdata()); } } @@ -51,7 +51,7 @@ impl Component for Image { fn bounds(&self, sink: &mut dyn FnMut(Rect)) { sink(Rect::from_center_and_size( self.area.center(), - self.toif.size, + self.toif.size(), )); } } @@ -101,12 +101,12 @@ impl Component for BlendedImage { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { - self.bg_top_left = self.bg.toif.size.snap(bounds.center(), CENTER); + self.bg_top_left = self.bg.toif.size().snap(bounds.center(), CENTER); - let ft_top_left = self.fg.toif.size.snap(bounds.center(), CENTER); + let ft_top_left = self.fg.toif.size().snap(bounds.center(), CENTER); self.fg_offset = ft_top_left - self.bg_top_left; - Rect::from_top_left_and_size(self.bg_top_left, self.bg.toif.size) + Rect::from_top_left_and_size(self.bg_top_left, self.bg.toif.size()) } fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option { @@ -120,7 +120,7 @@ impl Component for BlendedImage { fn bounds(&self, sink: &mut dyn FnMut(Rect)) { sink(Rect::from_top_left_and_size( self.bg_top_left, - self.bg.toif.size, + self.bg.toif.size(), )); } } diff --git a/core/embed/rust/src/ui/display/loader.rs b/core/embed/rust/src/ui/display/loader.rs index 3ff3c76cb..1025c27ef 100644 --- a/core/embed/rust/src/ui/display/loader.rs +++ b/core/embed/rust/src/ui/display/loader.rs @@ -43,10 +43,11 @@ pub fn loader_uncompress( const ICON_MAX_SIZE: i16 = constant::LOADER_ICON_MAX_SIZE; if let Some((icon, color)) = icon { - if icon.toif.width() <= ICON_MAX_SIZE && icon.toif.height() <= ICON_MAX_SIZE { + let toif_size = icon.toif.size(); + if toif_size.x <= ICON_MAX_SIZE && toif_size.y <= ICON_MAX_SIZE { let mut icon_data = [0_u8; ((ICON_MAX_SIZE * ICON_MAX_SIZE) / 2) as usize]; icon.toif.uncompress(&mut icon_data); - let i = Some((icon, color, icon.toif.size)); + let i = Some((icon, color, toif_size)); loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, i); } else { loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, None); @@ -237,7 +238,7 @@ pub fn loader_rust( let x_i = x_c - icon_area.x0; let y_i = y_c - icon_area.y0; - let data = unwrap!(icon_data).data() + let data = unwrap!(icon_data).zdata() [(((x_i & 0xFE) + (y_i * icon_width)) / 2) as usize]; if (x_i & 0x01) == 0 { underlying_color = icon_colortable[(data & 0xF) as usize]; @@ -337,7 +338,7 @@ pub fn loader_rust( icon_buffer_used.buffer[icon_offset as usize..(icon_offset + icon_width / 2) as usize] .copy_from_slice( - &unwrap!(icon_data).toif.data[(y_i * (icon_width / 2)) as usize + &unwrap!(icon_data).toif.zdata()[(y_i * (icon_width / 2)) as usize ..((y_i + 1) * (icon_width / 2)) as usize], ); icon_buffer = icon_buffer_used; diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index fb87f1690..e85e41aee 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -24,11 +24,10 @@ use crate::{ error::Error, time::Duration, trezorhal::{display, qr, time, uzlib::UzlibContext}, - ui::lerp::Lerp, + ui::{component::image::Image, lerp::Lerp}, }; use core::slice; -use crate::ui::component::image::Image; pub use crate::ui::display::toif::Icon; #[cfg(any(feature = "model_tt", feature = "model_tr"))] pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN}; @@ -228,8 +227,9 @@ pub fn rect_rounded2_partial( let mut icon_width = 0; if let Some((icon, icon_color)) = icon { - if icon.toif.width() <= MAX_ICON_SIZE && icon.toif.height() <= MAX_ICON_SIZE { - icon_area = Rect::from_center_and_size(center, icon.toif.size); + let icon_size = icon.toif.size(); + if icon_size.x <= MAX_ICON_SIZE && icon_size.y <= MAX_ICON_SIZE { + icon_area = Rect::from_center_and_size(center, icon_size); icon_area_clamped = icon_area.clamp(constant::screen()); icon.toif.uncompress(&mut icon_data); icon_colortable = get_color_table(icon_color, bg_color); @@ -461,10 +461,10 @@ pub fn text_over_image( empty_img.buffer.copy_from_slice(&img1.buffer); area = a; - r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, image.toif.size); + r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, image.toif.size()); offset_img_final = offset_img; } else { - area = Rect::from_top_left_and_size(offset_img.into(), image.toif.size); + area = Rect::from_top_left_and_size(offset_img.into(), image.toif.size()); r_img = area; offset_img_final = Offset::zero(); } @@ -583,26 +583,26 @@ pub fn icon_over_icon( let final_offset_bg; if let Some(a) = bg_area { area = a; - r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, icon_bg.toif.size); + r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, icon_bg.toif.size()); final_offset_bg = offset_bg; } else { r_bg = - Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), icon_bg.toif.size); + Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), icon_bg.toif.size()); area = r_bg; final_offset_bg = Offset::zero(); } - let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, icon_fg.toif.size); + let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, icon_fg.toif.size()); let clamped = area.clamp(constant::screen()).ensure_even_width(); set_window(clamped); let mut window_bg = [0; UZLIB_WINDOW_SIZE]; - let mut ctx_bg = UzlibContext::new(icon_bg.toif.data, Some(&mut window_bg)); + let mut ctx_bg = UzlibContext::new(icon_bg.toif.zdata(), Some(&mut window_bg)); let mut window_fg = [0; UZLIB_WINDOW_SIZE]; - let mut ctx_fg = UzlibContext::new(icon_fg.toif.data, Some(&mut window_fg)); + let mut ctx_fg = UzlibContext::new(icon_fg.toif.zdata(), Some(&mut window_fg)); dma2d_setup_4bpp_over_4bpp(color_icon_bg.into(), bg_color.into(), color_icon_fg.into()); diff --git a/core/embed/rust/src/ui/display/toif.rs b/core/embed/rust/src/ui/display/toif.rs index 9b57043b5..2da23f0b9 100644 --- a/core/embed/rust/src/ui/display/toif.rs +++ b/core/embed/rust/src/ui/display/toif.rs @@ -1,5 +1,4 @@ use crate::{ - trezorhal, trezorhal::{ display::ToifFormat, uzlib::{UzlibContext, UZLIB_WINDOW_SIZE}, @@ -15,22 +14,8 @@ use super::Color; const TOIF_HEADER_LENGTH: usize = 12; -pub fn toif_info(data: &[u8]) -> Option<(Offset, ToifFormat)> { - if let Ok(info) = trezorhal::display::toif_info(data) { - Some(( - Offset::new( - unwrap!(info.width.try_into()), - unwrap!(info.height.try_into()), - ), - info.format, - )) - } else { - None - } -} - pub fn icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color) { - let r = Rect::from_center_and_size(center, icon.toif.size); + let r = Rect::from_center_and_size(center, icon.toif.size()); let area = r.translate(get_offset()); let clamped = area.clamp(constant::screen()); let colortable = get_color_table(fg_color, bg_color); @@ -65,29 +50,49 @@ pub fn icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color) { } /// Holding toif data and allowing it to draw itself. +/// See https://docs.trezor.io/trezor-firmware/misc/toif.html for data format. #[derive(PartialEq, Eq, Clone, Copy)] pub struct Toif { - pub data: &'static [u8], - pub size: Offset, - pub format: ToifFormat, + data: &'static [u8], } impl Toif { - pub fn new(data: &'static [u8]) -> Self { - let info = unwrap!(toif_info(data)); - Self { - data: data[TOIF_HEADER_LENGTH..].as_ref(), - size: info.0, - format: info.1, + pub const fn new(data: &'static [u8]) -> Option { + if data.len() < TOIF_HEADER_LENGTH || data[0] != b'T' || data[1] != b'O' || data[2] != b'I' + { + return None; + } + let zdatalen = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize; + if zdatalen + TOIF_HEADER_LENGTH != data.len() { + return None; + } + Some(Self { data }) + } + + pub const fn format(&self) -> ToifFormat { + match self.data[3] { + b'f' => ToifFormat::FullColorBE, + b'g' => ToifFormat::GrayScaleOH, + b'F' => ToifFormat::FullColorLE, + b'G' => ToifFormat::GrayScaleEH, + _ => panic!(), } } - pub fn width(&self) -> i16 { - self.size.x + pub const fn width(&self) -> i16 { + u16::from_le_bytes([self.data[4], self.data[5]]) as i16 + } + + pub const fn height(&self) -> i16 { + u16::from_le_bytes([self.data[6], self.data[7]]) as i16 + } + + pub const fn size(&self) -> Offset { + Offset::new(self.width(), self.height()) } - pub fn height(&self) -> i16 { - self.size.y + pub fn zdata(&self) -> &'static [u8] { + &self.data[TOIF_HEADER_LENGTH..] } pub fn uncompress(&self, dest: &mut [u8]) { @@ -99,7 +104,7 @@ impl Toif { &'a self, window: Option<&'a mut [u8; UZLIB_WINDOW_SIZE]>, ) -> UzlibContext { - UzlibContext::new(self.data, window) + UzlibContext::new(self.zdata(), window) } } @@ -110,15 +115,15 @@ pub struct Icon { impl Icon { pub fn new(data: &'static [u8]) -> Self { - let toif = Toif::new(data); - assert!(toif.format == ToifFormat::GrayScaleEH); + let toif = unwrap!(Toif::new(data)); + assert!(toif.format() == ToifFormat::GrayScaleEH); Self { toif } } /// Display the icon with baseline Point, aligned according to the /// `alignment` argument. pub fn draw(&self, baseline: Point, alignment: Alignment2D, fg_color: Color, bg_color: Color) { - let r = Rect::snap(baseline, self.toif.size, alignment); + let r = Rect::snap(baseline, self.toif.size(), alignment); icon(self, r.center(), fg_color, bg_color); } } diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs index 6fd1fea43..70f91713b 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/render.rs @@ -131,10 +131,11 @@ fn homescreen_position_text( let text_width_clamped = text_width.clamp(0, screen().width()); let icon_size = if let Some(icon) = text.icon { - assert!(icon.toif.width() <= HOMESCREEN_MAX_ICON_SIZE); - assert!(icon.toif.height() <= HOMESCREEN_MAX_ICON_SIZE); + let size = icon.toif.size(); + assert!(size.x <= HOMESCREEN_MAX_ICON_SIZE); + assert!(size.y <= HOMESCREEN_MAX_ICON_SIZE); icon.toif.uncompress(icon_buffer); - icon.toif.size + size } else { Offset::zero() };