diff --git a/core/embed/rust/src/ui/shape/canvas/common.rs b/core/embed/rust/src/ui/shape/canvas/common.rs index ea2498390..66f41441b 100644 --- a/core/embed/rust/src/ui/shape/canvas/common.rs +++ b/core/embed/rust/src/ui/shape/canvas/common.rs @@ -5,7 +5,7 @@ use crate::ui::{ use super::super::{ utils::{circle_points, line_points, sin_f32}, - BitmapView, Viewport, + Bitmap, BitmapFormat, BitmapView, Viewport, }; #[cfg(feature = "ui_blurring")] @@ -71,6 +71,16 @@ pub trait BasicCanvas { fn draw_bitmap(&mut self, r: Rect, bitmap: BitmapView); } +pub trait CanvasBuilder<'a> { + /// Returns a format of the underlying bitmap. + fn format() -> BitmapFormat; + + /// Creates a new canvas from the specified bitmap. + /// + /// The bitmap must be mutable and have the same format as the canvas. + fn from_bitmap(bitmap: Bitmap<'a>) -> Self; +} + pub trait Canvas: BasicCanvas { /// Returns a non-mutable view of the underlying bitmap. fn view(&self) -> BitmapView; diff --git a/core/embed/rust/src/ui/shape/canvas/mod.rs b/core/embed/rust/src/ui/shape/canvas/mod.rs index 9e4507d6e..60820b5bc 100644 --- a/core/embed/rust/src/ui/shape/canvas/mod.rs +++ b/core/embed/rust/src/ui/shape/canvas/mod.rs @@ -4,7 +4,7 @@ mod rgb565; mod rgba8888; mod viewport; -pub use common::{BasicCanvas, Canvas}; +pub use common::{BasicCanvas, Canvas, CanvasBuilder}; pub use mono8::Mono8Canvas; pub use rgb565::Rgb565Canvas; pub use rgba8888::Rgba8888Canvas; diff --git a/core/embed/rust/src/ui/shape/canvas/mono8.rs b/core/embed/rust/src/ui/shape/canvas/mono8.rs index 429c28869..01e4085e2 100644 --- a/core/embed/rust/src/ui/shape/canvas/mono8.rs +++ b/core/embed/rust/src/ui/shape/canvas/mono8.rs @@ -8,7 +8,7 @@ use crate::{ use super::{ super::{Bitmap, BitmapFormat, BitmapView}, - BasicCanvas, Canvas, Viewport, + BasicCanvas, Canvas, CanvasBuilder, Viewport, }; #[cfg(feature = "ui_blurring")] @@ -74,6 +74,18 @@ impl<'a> BasicCanvas for Mono8Canvas<'a> { } } +impl<'a> CanvasBuilder<'a> for Mono8Canvas<'a> { + fn format() -> BitmapFormat { + BitmapFormat::MONO8 + } + + fn from_bitmap(bitmap: Bitmap<'a>) -> Self { + assert!(bitmap.format() == Self::format()); + let viewport = Viewport::from_size(bitmap.size()); + Self { bitmap, viewport } + } +} + impl<'a> Canvas for Mono8Canvas<'a> { fn view(&self) -> BitmapView { BitmapView::new(&self.bitmap) diff --git a/core/embed/rust/src/ui/shape/canvas/rgb565.rs b/core/embed/rust/src/ui/shape/canvas/rgb565.rs index db0b0ae0a..e5f189249 100644 --- a/core/embed/rust/src/ui/shape/canvas/rgb565.rs +++ b/core/embed/rust/src/ui/shape/canvas/rgb565.rs @@ -8,7 +8,7 @@ use crate::{ use super::{ super::{Bitmap, BitmapFormat, BitmapView}, - BasicCanvas, Canvas, Viewport, + BasicCanvas, Canvas, CanvasBuilder, Viewport, }; #[cfg(feature = "ui_blurring")] @@ -74,6 +74,18 @@ impl<'a> BasicCanvas for Rgb565Canvas<'a> { } } +impl<'a> CanvasBuilder<'a> for Rgb565Canvas<'a> { + fn format() -> BitmapFormat { + BitmapFormat::RGB565 + } + + fn from_bitmap(bitmap: Bitmap<'a>) -> Self { + assert!(bitmap.format() == Self::format()); + let viewport = Viewport::from_size(bitmap.size()); + Self { bitmap, viewport } + } +} + impl<'a> Canvas for Rgb565Canvas<'a> { fn view(&self) -> BitmapView { BitmapView::new(&self.bitmap) diff --git a/core/embed/rust/src/ui/shape/canvas/rgba8888.rs b/core/embed/rust/src/ui/shape/canvas/rgba8888.rs index f58be3c84..46189c7be 100644 --- a/core/embed/rust/src/ui/shape/canvas/rgba8888.rs +++ b/core/embed/rust/src/ui/shape/canvas/rgba8888.rs @@ -8,7 +8,7 @@ use crate::{ use super::{ super::{Bitmap, BitmapFormat, BitmapView}, - BasicCanvas, Canvas, Viewport, + BasicCanvas, Canvas, CanvasBuilder, Viewport, }; #[cfg(feature = "ui_blurring")] @@ -74,6 +74,18 @@ impl<'a> BasicCanvas for Rgba8888Canvas<'a> { } } +impl<'a> CanvasBuilder<'a> for Rgba8888Canvas<'a> { + fn format() -> BitmapFormat { + BitmapFormat::RGBA8888 + } + + fn from_bitmap(bitmap: Bitmap<'a>) -> Self { + assert!(bitmap.format() == Self::format()); + let viewport = Viewport::from_size(bitmap.size()); + Self { bitmap, viewport } + } +} + impl<'a> Canvas for Rgba8888Canvas<'a> { fn view(&self) -> BitmapView { BitmapView::new(&self.bitmap) diff --git a/core/embed/rust/src/ui/shape/mod.rs b/core/embed/rust/src/ui/shape/mod.rs index b42be4cc1..001971637 100644 --- a/core/embed/rust/src/ui/shape/mod.rs +++ b/core/embed/rust/src/ui/shape/mod.rs @@ -21,7 +21,9 @@ pub use bitmap::{Bitmap, BitmapFormat, BitmapView}; #[cfg(feature = "ui_blurring")] pub use blur::Blurring; pub use cache::drawing_cache::DrawingCache; -pub use canvas::{BasicCanvas, Canvas, Mono8Canvas, Rgb565Canvas, Rgba8888Canvas, Viewport}; +pub use canvas::{ + BasicCanvas, Canvas, CanvasBuilder, Mono8Canvas, Rgb565Canvas, Rgba8888Canvas, Viewport, +}; pub use circle::Circle; pub use display::render_on_display; #[cfg(feature = "ui_jpeg_decoder")] @@ -30,4 +32,4 @@ pub use qrcode::QrImage; pub use render::{DirectRenderer, ProgressiveRenderer, Renderer}; pub use text::Text; pub use toif::ToifImage; -pub use utils::PI4; +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 new file mode 100644 index 000000000..f8f7678c3 --- /dev/null +++ b/core/embed/rust/src/ui/shape/utils/imagebuf.rs @@ -0,0 +1,83 @@ +use crate::ui::{ + geometry::Offset, + shape::{Bitmap, BitmapView, Canvas, CanvasBuilder}, +}; + +/// Size of image buffer in bytes +const IMAGE_BUFFER_SIZE: usize = 64 * 1024; + +#[repr(align(16))] +struct AlignedBuffer { + bytes: [u8; IMAGE_BUFFER_SIZE], +} + +/// Raw image buffer used by `ImageBuffer` instances. + +static mut IMAGE_BUFFER: AlignedBuffer = AlignedBuffer { + bytes: [0; IMAGE_BUFFER_SIZE], +}; + +/// Set to `true` if the `IMAGE_BUFFER` is locked +/// (in use by some instance of `ImageBuffer`) +static mut IMAGE_BUFFER_LOCKED: bool = false; + +/// A struct representing an image buffer - generic buffer for mutable images. +pub struct ImageBuffer> { + canvas: T, +} + +impl Drop for ImageBuffer +where + T: Canvas + CanvasBuilder<'static>, +{ + fn drop(&mut self) { + // It's safe to modify static variable as whole app + // is single-threaded. + unsafe { + IMAGE_BUFFER_LOCKED = false; + } + } +} + +impl ImageBuffer +where + T: Canvas + CanvasBuilder<'static>, +{ + /// Creates a new image buffer with the specified format and size. + /// + /// Returns `None` if the buffer is already in use or the + /// buffer is not big enough to hold the image. + pub fn new(size: Offset) -> Option { + // SAFETY: + // It's safe to read/modify mutable static variable as + // whole app is single-threaded. + // + // We can be sure that `IMAGE_BUFFER` is not shared between + // multiple instances of `ImageBuffer` as we have `IMAGE_BUFFER_LOCKED` + // to prevent that. + unsafe { + if IMAGE_BUFFER_LOCKED { + return None; + } + + let bitmap = + Bitmap::new_mut(T::format(), None, size, None, &mut IMAGE_BUFFER.bytes[..])?; + + IMAGE_BUFFER_LOCKED = true; + + Some(Self { + canvas: T::from_bitmap(bitmap), + }) + } + } + + /// Returns the canvas for the bitmap in the image buffer. + pub fn canvas(&mut self) -> &mut T { + &mut self.canvas + } + + /// Returns the immutable view of the bitmap in the image buffer. + pub fn view(&self) -> BitmapView { + self.canvas.view() + } +} diff --git a/core/embed/rust/src/ui/shape/utils/mod.rs b/core/embed/rust/src/ui/shape/utils/mod.rs index 0b9c296cd..d637054d0 100644 --- a/core/embed/rust/src/ui/shape/utils/mod.rs +++ b/core/embed/rust/src/ui/shape/utils/mod.rs @@ -1,9 +1,11 @@ mod blur; mod circle; +mod imagebuf; mod line; mod trigo; pub use blur::{BlurAlgorithm, BlurBuff}; pub use circle::circle_points; +pub use imagebuf::ImageBuffer; pub use line::line_points; pub use trigo::sin_f32;