diff --git a/core/embed/rust/src/ui/model_mercury/cshape/mod.rs b/core/embed/rust/src/ui/model_mercury/cshape/mod.rs new file mode 100644 index 000000000..0154be1fb --- /dev/null +++ b/core/embed/rust/src/ui/model_mercury/cshape/mod.rs @@ -0,0 +1,3 @@ +mod unlock_overlay; + +pub use unlock_overlay::UnlockOverlay; diff --git a/core/embed/rust/src/ui/model_mercury/cshape/unlock_overlay.rs b/core/embed/rust/src/ui/model_mercury/cshape/unlock_overlay.rs new file mode 100644 index 000000000..36dbfde06 --- /dev/null +++ b/core/embed/rust/src/ui/model_mercury/cshape/unlock_overlay.rs @@ -0,0 +1,115 @@ +use crate::ui::{ + display::Color, + geometry::{Offset, Point, Rect}, + shape::{Canvas, DrawingCache, Mono8Canvas, Renderer, Shape, ShapeClone}, +}; + +use without_alloc::alloc::LocalAllocLeakExt; + +/// A special shape for rendering 7 differently rotated circular +/// sectors on 5 concentric circles, used as an overlay on the background +/// image on the lock screen. +/// +/// The overlay covers area of 170x170 pixels (centered at `pos`). +pub struct UnlockOverlay { + /// Center of the overlay + pos: Point, + // Angle of the rotation (in degrees) + angle: f32, +} + +impl UnlockOverlay { + /// Outer radius + pub const RADIUS: i16 = 85; + /// Distance between the circles + pub const SPAN: i16 = 10; + /// Thickness of the circles + pub const THICKNESS: i16 = 4; + + /// Create a new overlay with the given center and rotation angle. + pub fn new(pos: Point, angle: f32) -> Self { + Self { pos, angle } + } + + pub fn render<'a>(self, renderer: &mut impl Renderer<'a>) { + renderer.render_shape(self); + } + + fn prepare_overlay(&self, canvas: &mut dyn Canvas) { + let center = canvas.bounds().center(); + + let transp = Color::black(); + let opaque = Color::white(); + + canvas.fill_background(opaque); + + // The most outer circle (with two sectors) + let angle = self.angle; + let r = Self::RADIUS; + canvas.fill_sector(center, r, 0.0 + angle, 140.0 + angle, transp); + canvas.fill_sector(center, r, 235.0 + angle, 270.0 + angle, transp); + canvas.fill_circle(center, r - Self::THICKNESS, opaque, 255); + + // The second circle (with one sector) + let angle = -self.angle - 20.0; + let r = Self::RADIUS - Self::SPAN; + canvas.fill_sector(center, r, 0.0 + angle, 135.0 + angle, transp); + canvas.fill_circle(center, r - Self::THICKNESS, opaque, 255); + + // The third circle (with one sector) + let angle = self.angle / 2.0 + 90.0; + let r = Self::RADIUS - 2 * Self::SPAN; + canvas.fill_sector(center, r, 0.0 + angle, 270.0 + angle, transp); + canvas.fill_circle(center, r - Self::THICKNESS, opaque, 255); + + // The fourth circle (with two sectors) + let angle = -self.angle / 2.0 + 60.0; + let r = Self::RADIUS - 3 * Self::SPAN; + canvas.fill_sector(center, r, 0.0 + angle, 110.0 + angle, transp); + canvas.fill_sector(center, r, 180.0 + angle, 280.0 + angle, transp); + canvas.fill_circle(center, r - Self::THICKNESS, opaque, 255); + + // Innner fixed circle + let r = Self::RADIUS - (9 * Self::SPAN) / 2; + canvas.fill_circle(center, r, transp, 255); + canvas.fill_circle(center, r - Self::THICKNESS, opaque, 255); + } +} + +impl<'a> Shape<'a> for UnlockOverlay { + fn bounds(&self) -> Rect { + Rect::new( + self.pos - Offset::uniform(Self::RADIUS), + self.pos + Offset::uniform(Self::RADIUS + 1), + ) + } + + fn cleanup(&mut self, _cache: &DrawingCache<'a>) { + // TODO: inform the cache that we won't use the zlib slot anymore + } + + fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache<'a>) { + let bounds = self.bounds(); + + let overlay_buff = &mut unwrap!(cache.image_buff(), "No image buffer"); + + let mut overlay_canvas = unwrap!( + Mono8Canvas::new(bounds.size(), None, None, &mut overlay_buff[..]), + "Too small buffer" + ); + + self.prepare_overlay(&mut overlay_canvas); + + canvas.blend_bitmap(bounds, overlay_canvas.view().with_fg(Color::black())); + } +} + +impl<'a> ShapeClone<'a> for UnlockOverlay { + fn clone_at_bump(self, bump: &'a T) -> Option<&'a mut dyn Shape<'a>> + where + T: LocalAllocLeakExt<'a>, + { + let clone = bump.alloc_t()?; + Some(clone.uninit.init(UnlockOverlay { ..self })) + } +} diff --git a/core/embed/rust/src/ui/shape/cache/drawing_cache.rs b/core/embed/rust/src/ui/shape/cache/drawing_cache.rs index 1ac40bcd6..7b4d01780 100644 --- a/core/embed/rust/src/ui/shape/cache/drawing_cache.rs +++ b/core/embed/rust/src/ui/shape/cache/drawing_cache.rs @@ -17,6 +17,10 @@ const ZLIB_CACHE_SLOTS: usize = 1; const ZLIB_CACHE_SLOTS: usize = 3; const RENDER_BUFF_SIZE: usize = (240 * 2 * 16) + ALIGN_PAD; + +#[cfg(feature = "model_mercury")] +const IMAGE_BUFF_SIZE: usize = 32768 + ALIGN_PAD; +#[cfg(not(feature = "model_mercury"))] const IMAGE_BUFF_SIZE: usize = 2048 + ALIGN_PAD; pub type ImageBuff = [u8; IMAGE_BUFF_SIZE]; diff --git a/core/embed/rust/src/ui/shape/mod.rs b/core/embed/rust/src/ui/shape/mod.rs index c3bb827bd..15aff8319 100644 --- a/core/embed/rust/src/ui/shape/mod.rs +++ b/core/embed/rust/src/ui/shape/mod.rs @@ -34,4 +34,5 @@ pub use rawimage::RawImage; pub use render::{DirectRenderer, ProgressiveRenderer, Renderer}; pub use text::Text; pub use toif::ToifImage; +#[cfg(feature = "model_mercury")] pub use utils::ImageBuffer; diff --git a/core/embed/rust/src/ui/shape/utils/imagebuf.rs b/core/embed/rust/src/ui/shape/utils/imagebuf.rs index f8f7678c3..ee9386547 100644 --- a/core/embed/rust/src/ui/shape/utils/imagebuf.rs +++ b/core/embed/rust/src/ui/shape/utils/imagebuf.rs @@ -4,6 +4,7 @@ use crate::ui::{ }; /// Size of image buffer in bytes +/// (up to 180x180 pixel, 16-bit RGB565 image) const IMAGE_BUFFER_SIZE: usize = 64 * 1024; #[repr(align(16))] diff --git a/core/embed/rust/src/ui/shape/utils/mod.rs b/core/embed/rust/src/ui/shape/utils/mod.rs index d637054d0..1d374a7fb 100644 --- a/core/embed/rust/src/ui/shape/utils/mod.rs +++ b/core/embed/rust/src/ui/shape/utils/mod.rs @@ -6,6 +6,9 @@ mod trigo; pub use blur::{BlurAlgorithm, BlurBuff}; pub use circle::circle_points; + +#[cfg(feature = "model_mercury")] pub use imagebuf::ImageBuffer; + pub use line::line_points; pub use trigo::sin_f32;