diff --git a/core/embed/rust/src/ui/display/loader.rs b/core/embed/rust/src/ui/display/loader/circular.rs similarity index 87% rename from core/embed/rust/src/ui/display/loader.rs rename to core/embed/rust/src/ui/display/loader/circular.rs index 1025c27ef..000905e67 100644 --- a/core/embed/rust/src/ui/display/loader.rs +++ b/core/embed/rust/src/ui/display/loader/circular.rs @@ -3,7 +3,6 @@ use crate::ui::{ display::Color, geometry::{Offset, Point, Rect}, }; -use core::slice::from_raw_parts; #[cfg(feature = "dma2d")] use crate::trezorhal::{ @@ -16,9 +15,6 @@ use crate::ui::{ display::toif::Icon, }; -pub const LOADER_MIN: u16 = 0; -pub const LOADER_MAX: u16 = 1000; - const LOADER_SIZE: i32 = (LOADER_OUTER * 2.0) as i32; const OUTER: f32 = constant::LOADER_OUTER; @@ -32,7 +28,7 @@ const INNER_OUTER_ANTI: i32 = ((INNER + 2.5) * (INNER + 2.5)) as i32; const OUTER_OUT_ANTI: i32 = ((OUTER - 1.5) * (OUTER - 1.5)) as i32; const OUTER_MAX: i32 = ((OUTER - 0.5) * (OUTER - 0.5)) as i32; -pub fn loader_uncompress( +pub fn loader_circular_uncompress( y_offset: i16, fg_color: Color, bg_color: Color, @@ -47,7 +43,7 @@ pub fn loader_uncompress( 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, toif_size)); + let i = Some((icon_data.as_ref(), 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); @@ -57,29 +53,24 @@ pub fn loader_uncompress( } } -#[no_mangle] -pub extern "C" fn loader_uncompress_r( - y_offset: cty::int32_t, - fg_color: cty::uint16_t, - bg_color: cty::uint16_t, - icon_color: cty::uint16_t, - progress: cty::int32_t, - indeterminate: cty::int32_t, - icon_data: cty::uintptr_t, - icon_data_size: cty::uint32_t, +pub fn loader_circular( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, ) { - let fg = Color::from_u16(fg_color); - let bg = Color::from_u16(bg_color); - let ic_color = Color::from_u16(icon_color); - - let i = if icon_data != 0 { - let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) }; - Some((Icon::new(data_slice), ic_color)) - } else { - None - }; + loader_circular_uncompress(y_offset, fg_color, bg_color, progress, false, icon); +} - loader_uncompress(y_offset as _, fg, bg, progress as _, indeterminate != 0, i); +pub fn loader_circular_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + loader_circular_uncompress(y_offset, fg_color, bg_color, progress, true, icon); } #[inline(always)] @@ -189,7 +180,7 @@ pub fn loader_rust( bg_color: Color, progress: u16, indeterminate: bool, - icon: Option<(Icon, Color, Offset)>, + icon: Option<(&[u8], Color, Offset)>, ) { let center = screen().center() + Offset::new(0, y_offset); let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2)); @@ -205,14 +196,14 @@ pub fn loader_rust( let mut icon_area = Rect::zero(); let mut icon_area_clamped = Rect::zero(); let mut icon_width = 0; - let mut icon_data = None; + let mut icon_data = [].as_ref(); if let Some((data, color, size)) = icon { if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE { icon_width = size.x; icon_area = Rect::from_center_and_size(center, size); icon_area_clamped = icon_area.clamp(constant::screen()); - icon_data = Some(data); + icon_data = data; use_icon = true; icon_colortable = display::get_color_table(color, bg_color); } @@ -238,8 +229,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).zdata() - [(((x_i & 0xFE) + (y_i * icon_width)) / 2) as usize]; + let data = icon_data[(((x_i & 0xFE) + (y_i * icon_width)) / 2) as usize]; if (x_i & 0x01) == 0 { underlying_color = icon_colortable[(data & 0xF) as usize]; } else { @@ -270,7 +260,7 @@ pub fn loader_rust( bg_color: Color, progress: u16, indeterminate: bool, - icon: Option<(Icon, Color, Offset)>, + icon: Option<(&[u8], Color, Offset)>, ) { let center = screen().center() + Offset::new(0, y_offset); let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2)); @@ -285,16 +275,16 @@ pub fn loader_rust( let mut icon_width = 0; let mut icon_offset = 0; let mut icon_color = Color::from_u16(0); - let mut icon_data = None; + let mut icon_data = [].as_ref(); - if let Some((icon, color, size)) = icon { + if let Some((data, color, size)) = icon { if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE { icon_width = size.x; icon_area = Rect::from_center_and_size(center, size); icon_area_clamped = icon_area.clamp(constant::screen()); icon_offset = (icon_area_clamped.x0 - r.x0) / 2; icon_color = color; - icon_data = Some(icon); + icon_data = data; use_icon = true; } } @@ -338,7 +328,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.zdata()[(y_i * (icon_width / 2)) as usize + &icon_data[(y_i * (icon_width / 2)) as usize ..((y_i + 1) * (icon_width / 2)) as usize], ); icon_buffer = icon_buffer_used; @@ -371,23 +361,3 @@ pub fn loader_rust( dma2d_wait_for_transfer(); } - -pub fn loader( - progress: u16, - y_offset: i16, - fg_color: Color, - bg_color: Color, - icon: Option<(Icon, Color)>, -) { - loader_uncompress(y_offset, fg_color, bg_color, progress, false, icon); -} - -pub fn loader_indeterminate( - progress: u16, - y_offset: i16, - fg_color: Color, - bg_color: Color, - icon: Option<(Icon, Color)>, -) { - loader_uncompress(y_offset, fg_color, bg_color, progress, true, icon); -} diff --git a/core/embed/rust/src/ui/display/loader/mod.rs b/core/embed/rust/src/ui/display/loader/mod.rs new file mode 100644 index 000000000..12d445a40 --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/mod.rs @@ -0,0 +1,68 @@ +mod circular; +mod rectangular; +mod starry; + +use crate::ui::display::{Color, Icon}; +use core::slice::from_raw_parts; + +#[cfg(feature = "model_tt")] +use crate::ui::display::loader::circular::{ + loader_circular as determinate, loader_circular_indeterminate as indeterminate, +}; +#[cfg(not(feature = "model_tt"))] +use crate::ui::display::loader::rectangular::loader_rectangular as determinate; +#[cfg(not(feature = "model_tt"))] +use crate::ui::display::loader::starry::loader_starry_indeterminate as indeterminate; + +pub const LOADER_MIN: u16 = 0; +pub const LOADER_MAX: u16 = 1000; + +pub fn loader( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + determinate(progress, y_offset, fg_color, bg_color, icon); +} + +pub fn loader_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + indeterminate(progress, y_offset, fg_color, bg_color, icon); +} + +//TODO: remove when loader is no longer called from C +#[no_mangle] +pub extern "C" fn loader_uncompress_r( + y_offset: cty::int32_t, + fg_color: cty::uint16_t, + bg_color: cty::uint16_t, + icon_color: cty::uint16_t, + progress: cty::int32_t, + indeterminate: cty::int32_t, + icon_data: cty::uintptr_t, + icon_data_size: cty::uint32_t, +) { + let fg = Color::from_u16(fg_color); + let bg = Color::from_u16(bg_color); + let ic_color = Color::from_u16(icon_color); + + let i = if icon_data != 0 { + let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) }; + Some((Icon::new(data_slice), ic_color)) + } else { + None + }; + + if indeterminate == 0 { + loader(progress as _, y_offset as _, fg, bg, i); + } else { + loader_indeterminate(progress as _, y_offset as _, fg, bg, i); + } +} diff --git a/core/embed/rust/src/ui/display/loader/rectangular.rs b/core/embed/rust/src/ui/display/loader/rectangular.rs new file mode 100644 index 000000000..7ad16c57f --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/rectangular.rs @@ -0,0 +1,25 @@ +use crate::ui::{ + constant::{screen, LOADER_INNER}, + display, + display::{Color, Icon}, + geometry::{Offset, Rect}, +}; + +pub fn loader_rectangular( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + let area = Rect::from_center_and_size(screen().center(), Offset::uniform(LOADER_INNER as _)) + .translate(Offset::y(y_offset)); + + display::rect_rounded2_partial( + area, + fg_color, + bg_color, + (100 * progress as u32 / 1000) as i16, + icon, + ); +} diff --git a/core/embed/rust/src/ui/display/loader/starry.rs b/core/embed/rust/src/ui/display/loader/starry.rs new file mode 100644 index 000000000..2dbdd2f5f --- /dev/null +++ b/core/embed/rust/src/ui/display/loader/starry.rs @@ -0,0 +1,67 @@ +use crate::ui::{ + constant::{screen, LOADER_OUTER}, + display::{rect_fill, rect_fill_rounded, rect_fill_rounded1, Color, Icon}, + geometry::{Offset, Point, Rect, CENTER}, +}; +use core::f32::consts::SQRT_2; + +const SIZE_SMALL: i16 = 2; +const SIZE_MEDIUM: i16 = 4; +const SIZE_LARGE: i16 = 6; +const RADIUS: i16 = 13; +const DIAGONAL: i16 = ((RADIUS as f32 * SQRT_2) / 2_f32) as i16; + +fn star_small(center: Point, fg: Color, _bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_SMALL)); + rect_fill(r, fg); +} + +fn star_medium(center: Point, fg: Color, bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_MEDIUM)); + rect_fill_rounded1(r, fg, bg); +} + +fn star_large(center: Point, fg: Color, bg: Color) { + let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_LARGE)); + rect_fill_rounded(r, fg, bg, 2); +} + +pub fn loader_starry_indeterminate( + progress: u16, + y_offset: i16, + fg_color: Color, + bg_color: Color, + icon: Option<(Icon, Color)>, +) { + let area = Rect::from_center_and_size(screen().center(), Offset::uniform(LOADER_OUTER as _)) + .translate(Offset::y(y_offset)); + + rect_fill(area, bg_color); + + let coords = [ + Point::new(area.center().x, area.center().y - RADIUS), + Point::new(area.center().x + DIAGONAL, area.center().y - DIAGONAL), + Point::new(area.center().x + RADIUS, area.center().y), + Point::new(area.center().x + DIAGONAL, area.center().y + DIAGONAL), + Point::new(area.center().x, area.center().y + RADIUS), + Point::new(area.center().x - DIAGONAL, area.center().y + DIAGONAL), + Point::new(area.center().x - RADIUS, area.center().y), + Point::new(area.center().x - DIAGONAL, area.center().y - DIAGONAL), + ]; + + let big_idx = (progress / 125) as usize % 8; + + for (i, c) in coords.iter().enumerate() { + if i == big_idx { + star_large(*c, fg_color, bg_color); + } else if (big_idx + 1) % 8 == i || (big_idx - 1) % 8 == i { + star_medium(*c, fg_color, bg_color); + } else { + star_small(*c, fg_color, bg_color); + } + } + + if let Some((icon, color)) = icon { + icon.draw(area.center(), CENTER, color, bg_color); + } +} diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index 3a9ef3ed4..6e225ed19 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -1,4 +1,3 @@ -#[cfg(any(feature = "model_tt", feature = "model_tr"))] pub mod loader; #[cfg(feature = "jpeg")] pub mod tjpgd; diff --git a/core/embed/rust/src/ui/model_tr/constant.rs b/core/embed/rust/src/ui/model_tr/constant.rs index dc5f341e5..c4ce3780b 100644 --- a/core/embed/rust/src/ui/model_tr/constant.rs +++ b/core/embed/rust/src/ui/model_tr/constant.rs @@ -5,9 +5,9 @@ pub const HEIGHT: i16 = 128; pub const LINE_SPACE: i16 = 1; pub const FONT_BPP: i16 = 1; -pub const LOADER_OUTER: f32 = 20_f32; -pub const LOADER_INNER: f32 = 14_f32; -pub const LOADER_ICON_MAX_SIZE: i16 = 24; +pub const LOADER_OUTER: f32 = 32_f32; +pub const LOADER_INNER: f32 = 18_f32; +pub const LOADER_ICON_MAX_SIZE: i16 = 8; pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT)