1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-23 06:58:13 +00:00

fixup! feat(core): introduce new drawing library

This commit is contained in:
cepetr 2024-03-07 17:43:47 +01:00
parent 5cb2abd3c8
commit 434b272f44
15 changed files with 78 additions and 164 deletions

View File

@ -1,41 +0,0 @@
use crate::ui::{display::Color, geometry::Offset};
use super::{BasicCanvas, Canvas, Mono8Canvas};
use crate::trezorhal::display;
pub struct MonoDisplay<'a> {
canvas: Mono8Canvas<'a>,
}
impl<'a> MonoDisplay<'a> {
pub fn new(size: Offset, buff: &'a mut [u8]) -> Option<Self> {
Some(Self {
canvas: Mono8Canvas::new(size, None, buff)?,
})
}
pub fn canvas(&mut self) -> &mut Mono8Canvas<'a> {
&mut self.canvas
}
pub fn refresh(&self) {
// TODO:
let w = self.canvas.width() as u16 - 1;
let h = self.canvas.height() as u16 - 1;
display::set_window(0, 0, w, h);
let view = self.canvas.view();
for y in 0..self.canvas.size().y {
let row = unwrap!(view.row(y));
for value in row.iter() {
let c = Color::rgb(*value, *value, *value);
display::pixeldata(c.into());
}
}
display::refresh();
}
}

View File

@ -1,49 +0,0 @@
use crate::ui::{
display::Color,
geometry::{Offset, Rect},
};
use super::{BasicCanvas, BitmapView, Viewport};
use crate::trezorhal::bitmap::Dma2d;
// ==========================================================================
// Display canvas
// ==========================================================================
pub struct Wnd565Display {
size: Offset,
viewport: Viewport,
}
impl Wnd565Display {
pub fn acquire() -> Option<Self> {
let size = Offset::new(240, 240); // TODO
let viewport = Viewport::from_size(size);
Some(Self { size, viewport })
}
}
impl BasicCanvas for Wnd565Display {
fn viewport(&self) -> Viewport {
self.viewport
}
fn set_viewport(&mut self, viewport: Viewport) {
self.viewport = viewport.absolute_clip(self.bounds());
}
fn size(&self) -> Offset {
self.size
}
fn fill_rect(&mut self, r: Rect, color: Color) {
let r = r.translate(self.viewport.origin);
Dma2d::wnd565_fill(r, self.viewport.clip, color);
}
fn draw_bitmap(&mut self, r: Rect, bitmap: BitmapView) {
let r = r.translate(self.viewport.origin);
Dma2d::wnd565_copy(r, self.viewport.clip, &bitmap);
}
}

View File

@ -51,12 +51,12 @@ impl Bar {
Self { thickness, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for Bar {
impl Shape<'_> for Bar {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
self.area
}
@ -110,8 +110,8 @@ impl Shape for Bar {
}
}
impl ShapeClone for Bar {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'s> ShapeClone<'s> for Bar {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -16,23 +16,23 @@ use without_alloc::alloc::LocalAllocLeakExt;
///
/// `Shape` objects may use `DrawingCache` as a scratch-pad memory or for
/// caching expensive calculations results.
pub trait Shape {
pub trait Shape<'s> {
/// Returns the smallest bounding rectangle containing whole parts of the
/// shape.
///
/// The function is used by renderer for optimization if the shape
/// must be renderer or not.
fn bounds(&self, cache: &DrawingCache) -> Rect;
fn bounds(&self, cache: &DrawingCache<'s>) -> Rect;
/// Draws shape on the canvas.
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache);
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache<'s>);
/// The function should release all allocated resources needed
/// for shape drawing.
///
/// It's called by renderer if the shape's draw() function won't be called
/// anymore.
fn cleanup(&mut self, cache: &DrawingCache);
fn cleanup(&mut self, cache: &DrawingCache<'s>);
}
// ==========================================================================
@ -41,12 +41,13 @@ pub trait Shape {
/// All shapes (like `Bar`, `Text`, `Circle`, ...) that can be rendered
/// by `ProgressiveRender` must implement `ShapeClone`.
pub trait ShapeClone {
pub trait ShapeClone<'s> {
/// Clones a shape object at the specified memory bump.
///
/// The method is used by `ProgressiveRenderer` to store shape objects for
/// deferred drawing.
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>;
T: LocalAllocLeakExt<'alloc>,
'alloc: 's;
}

View File

@ -17,12 +17,12 @@ impl Blurring {
Self { area, radius }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for Blurring {
impl Shape<'_> for Blurring {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
self.area
}
@ -34,8 +34,8 @@ impl Shape for Blurring {
}
}
impl ShapeClone for Blurring {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'s> ShapeClone<'s> for Blurring {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -160,7 +160,7 @@ impl<'a> JpegCacheSlot<'a> {
}
let decoder = unwrap!(self.decoder.as_mut()); // should never fail
let input = unwrap!(self.input.as_mut()); // shoud never fail
let input = unwrap!(self.input.as_mut()); // should never fail
let mut output = JpegFnOutput::new(output);
match decoder.decomp2(input, &mut output) {

View File

@ -36,7 +36,7 @@ impl<'a> ZlibCacheSlot<'a> {
}
/// May be called with zdata == &[] to make the slot free
fn reset(&mut self, zdata: &'static [u8]) {
fn reset(&mut self, zdata: &'a [u8]) {
// Drop the existing decompression context holding
// a mutable reference to window buffer
self.dc = None;
@ -133,7 +133,7 @@ impl<'a> ZlibCache<'a> {
pub fn uncompress(
&mut self,
zdata: &'static [u8],
zdata: &'a [u8],
offset: usize,
dest_buf: &mut [u8],
) -> Result<bool, ()> {
@ -155,7 +155,7 @@ impl<'a> ZlibCache<'a> {
pub fn uncompress_toif(
&mut self,
toif: Toif<'static>,
toif: Toif<'a>,
from_row: i16,
dest_buf: &mut [u8],
) -> Result<(), ()> {

View File

@ -64,12 +64,12 @@ impl Circle {
}
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
}
impl Shape for Circle {
impl Shape<'_> for Circle {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
let c = self.center;
let r = self.radius;
@ -129,8 +129,8 @@ impl Shape for Circle {
}
}
impl ShapeClone for Circle {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'s> ShapeClone<'s> for Circle {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -8,13 +8,13 @@ use super::{DrawingCache, Renderer, Shape, ShapeClone};
use without_alloc::alloc::LocalAllocLeakExt;
/// A shape for rendering compressed JPEG images.
pub struct JpegImage {
pub struct JpegImage<'a> {
/// Image position
pos: Point,
// Image position alignment
align: Alignment2D,
/// JPEG data
jpeg: &'static [u8],
jpeg: &'a [u8],
/// Scale factor (default 0)
scale: u8,
/// Blurring radius or 0 if no blurring required (default 0)
@ -26,8 +26,8 @@ pub struct JpegImage {
blur_tag: Option<u32>,
}
impl JpegImage {
pub fn new(pos: Point, jpeg: &'static [u8]) -> Self {
impl<'a> JpegImage<'a> {
pub fn new(pos: Point, jpeg: &'a [u8]) -> Self {
JpegImage {
pos,
align: Alignment2D::TOP_LEFT,
@ -59,18 +59,18 @@ impl JpegImage {
Self { dim, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render(self, renderer: &mut impl Renderer<'a>) {
renderer.render_shape(self);
}
}
impl Shape for JpegImage {
fn bounds(&self, cache: &DrawingCache) -> Rect {
impl<'a> Shape<'a> for JpegImage<'a> {
fn bounds(&self, cache: &DrawingCache<'a>) -> Rect {
let size = unwrap!(cache.jpeg().get_size(self.jpeg, self.scale), "Invalid JPEG");
Rect::from_top_left_and_size(size.snap(self.pos, self.align), size)
}
fn cleanup(&mut self, _cache: &DrawingCache) {
fn cleanup(&mut self, _cache: &DrawingCache<'a>) {
self.blur_tag = None;
}
@ -78,7 +78,7 @@ impl Shape for JpegImage {
// Faster implementation suitable for DirectRenderer without blurring support
// (but is terribly slow on ProgressiveRenderer if slices are not aligned
// to JPEG MCUs )
fn draw(&mut self, canvas: &mut dyn RgbCanvasEx, cache: &DrawingCache) {
fn draw(&mut self, canvas: &mut dyn RgbCanvasEx, cache: &DrawingCache<'a>) {
let bounds = self.bounds(cache);
let clip = canvas.viewport().relative_clip(bounds).clip;
@ -103,7 +103,7 @@ impl Shape for JpegImage {
}*/
// This is a little bit slower implementation suitable for ProgressiveRenderer
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache) {
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache<'a>) {
let bounds = self.bounds(cache);
let clip = canvas.viewport().relative_clip(bounds).clip;
@ -188,8 +188,8 @@ impl Shape for JpegImage {
}
}
impl ShapeClone for JpegImage {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'a> ShapeClone<'a> for JpegImage<'a> {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'a>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -8,9 +8,9 @@ use crate::ui::{
use static_alloc::Bump;
pub fn render_on_display<F>(clip: Option<Rect>, bg_color: Option<Color>, mut func: F)
pub fn render_on_display<'a, F>(clip: Option<Rect>, bg_color: Option<Color>, func: F)
where
F: FnMut(&mut DirectRenderer<Mono8Canvas>),
F: FnOnce(&mut DirectRenderer<'_, 'a, Mono8Canvas<'a>>),
{
// TODO: do not use constants 128 & 64 directly
@ -22,6 +22,8 @@ where
let bump = unsafe { &mut *core::ptr::addr_of_mut!(BUMP) };
{
bump.reset();
let cache = DrawingCache::new(bump, bump);
let mut canvas = unwrap!(Mono8Canvas::new(Offset::new(128, 64), None, fb));
@ -35,7 +37,6 @@ where
refresh_display(&canvas);
}
bump.reset();
}
fn refresh_display(canvas: &Mono8Canvas) {

View File

@ -9,9 +9,9 @@ use crate::trezorhal::bitmap::{BitmapView, Dma2d};
use static_alloc::Bump;
pub fn render_on_display<F>(clip: Option<Rect>, bg_color: Option<Color>, mut func: F)
pub fn render_on_display<'a, F>(clip: Option<Rect>, bg_color: Option<Color>, func: F)
where
F: FnMut(&mut ProgressiveRenderer<Bump<[u8; 40 * 1024]>, DisplayModelT>),
F: FnOnce(&mut ProgressiveRenderer<'_, 'a, Bump<[u8; 40 * 1024]>, DisplayModelT>),
{
#[link_section = ".no_dma_buffers"]
static mut BUMP_A: Bump<[u8; 40 * 1024]> = Bump::uninit();
@ -22,6 +22,9 @@ where
let bump_a = unsafe { &mut *core::ptr::addr_of_mut!(BUMP_A) };
let bump_b = unsafe { &mut *core::ptr::addr_of_mut!(BUMP_B) };
{
bump_a.reset();
bump_b.reset();
let cache = DrawingCache::new(bump_a, bump_b);
let mut canvas = DisplayModelT::acquire().unwrap();
@ -35,8 +38,6 @@ where
target.render(16);
}
bump_a.reset();
bump_b.reset();
}
pub struct DisplayModelT {

View File

@ -83,7 +83,7 @@ impl QrImage {
}
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
@ -107,7 +107,7 @@ impl QrImage {
}
}
impl Shape for QrImage {
impl Shape<'_> for QrImage {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
self.area
}
@ -157,8 +157,8 @@ impl Shape for QrImage {
}
}
impl ShapeClone for QrImage {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'s> ShapeClone<'s> for QrImage {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -14,7 +14,7 @@ use without_alloc::{alloc::LocalAllocLeakExt, FixedVec};
/// All renders must implement Renderer trait
/// Renderers can immediately use the draw() method of the passed shape or
/// may store it (using the boxed() method) and draw it later
pub trait Renderer {
pub trait Renderer<'a> {
fn viewport(&self) -> Viewport;
fn set_viewport(&mut self, viewport: Viewport);
@ -33,7 +33,7 @@ pub trait Renderer {
fn render_shape<S>(&mut self, shape: S)
where
S: Shape + ShapeClone;
S: Shape<'a> + ShapeClone<'a>;
fn in_window(&mut self, r: Rect, inner: &dyn Fn(&mut Self)) {
let original = self.set_window(r);
@ -60,13 +60,13 @@ pub trait Renderer {
// ==========================================================================
/// A simple implementation of a Renderer that draws directly onto the CanvasEx
pub struct DirectRenderer<'a, 'alloc: 'a, C>
pub struct DirectRenderer<'a, 'alloc, C>
where
C: Canvas,
{
/// Target canvas
canvas: &'a mut C,
/// Drawing che (decompression cache, scratch-pad memory)
/// Drawing cache (decompression context, scratch-pad memory)
cache: &'a DrawingCache<'alloc>,
}
@ -91,7 +91,7 @@ where
}
}
impl<'a, 'alloc, C> Renderer for DirectRenderer<'a, 'alloc, C>
impl<'a, 'alloc, C> Renderer<'alloc> for DirectRenderer<'a, 'alloc, C>
where
C: Canvas,
{
@ -105,7 +105,7 @@ where
fn render_shape<S>(&mut self, mut shape: S)
where
S: Shape + ShapeClone,
S: Shape<'alloc> + ShapeClone<'alloc>,
{
if self.canvas.viewport().contains(shape.bounds(self.cache)) {
shape.draw(self.canvas, self.cache);
@ -119,13 +119,14 @@ where
// ==========================================================================
struct ShapeHolder<'a> {
shape: &'a mut dyn Shape,
shape: &'a mut dyn Shape<'a>,
viewport: Viewport,
}
/// A more advanced Renderer implementation that supports deferred rendering.
pub struct ProgressiveRenderer<'a, 'alloc, T: LocalAllocLeakExt<'alloc>, C>
pub struct ProgressiveRenderer<'a, 'alloc, T, C>
where
T: LocalAllocLeakExt<'alloc>,
C: BasicCanvas,
{
/// Target canvas
@ -138,7 +139,7 @@ where
viewport: Viewport,
// Default background color
bg_color: Option<Color>,
/// Drawing cache (decompression cache, scratch-pad memory)
/// Drawing cache (decompression context, scratch-pad memory)
cache: &'a DrawingCache<'alloc>,
}
@ -218,7 +219,7 @@ where
}
}
impl<'a, 'alloc, T, C> Renderer for ProgressiveRenderer<'a, 'alloc, T, C>
impl<'a, 'alloc, T, C> Renderer<'alloc> for ProgressiveRenderer<'a, 'alloc, T, C>
where
T: LocalAllocLeakExt<'alloc>,
C: BasicCanvas,
@ -233,7 +234,7 @@ where
fn render_shape<S>(&mut self, shape: S)
where
S: Shape + ShapeClone,
S: Shape<'alloc> + ShapeClone<'alloc>,
{
// Is the shape visible?
if self.viewport.contains(shape.bounds(self.cache)) {

View File

@ -47,7 +47,7 @@ impl<'a> Text<'a> {
Self { align, ..self }
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render<'r>(self, renderer: &mut impl Renderer<'r>) {
renderer.render_shape(self);
}
@ -63,7 +63,7 @@ impl<'a> Text<'a> {
}
}
impl<'a> Shape for Text<'a> {
impl<'a> Shape<'_> for Text<'a> {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
let pos = self.aligned_pos();
let max_ascent = self.font.text_max_height() - self.font.text_baseline();
@ -105,8 +105,8 @@ impl<'a> Shape for Text<'a> {
}
}
impl<'a> ShapeClone for Text<'a> {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'a, 's> ShapeClone<'s> for Text<'a> {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'s>>
where
T: LocalAllocLeakExt<'alloc>,
{

View File

@ -9,21 +9,21 @@ use super::{DrawingCache, Renderer, Shape, ShapeClone};
use without_alloc::alloc::LocalAllocLeakExt;
/// A shape for rendering compressed TOIF images.
pub struct ToifImage {
pub struct ToifImage<'a> {
/// Image position
pos: Point,
// Image position alignment
align: Alignment2D,
// Image data
toif: Toif<'static>,
toif: Toif<'a>,
// Foreground color
fg_color: Color,
// Optional background color
bg_color: Option<Color>,
}
impl ToifImage {
pub fn new(pos: Point, toif: Toif<'static>) -> Self {
impl<'a> ToifImage<'a> {
pub fn new(pos: Point, toif: Toif<'a>) -> Self {
Self {
pos,
align: Alignment2D::TOP_LEFT,
@ -48,11 +48,11 @@ impl ToifImage {
}
}
pub fn render(self, renderer: &mut impl Renderer) {
pub fn render(self, renderer: &mut impl Renderer<'a>) {
renderer.render_shape(self);
}
fn draw_grayscale(&self, canvas: &mut dyn Canvas, cache: &DrawingCache) {
fn draw_grayscale(&self, canvas: &mut dyn Canvas, cache: &DrawingCache<'a>) {
// TODO: introduce new viewport/shape function for this calculation
let bounds = self.bounds(cache);
let viewport = canvas.viewport();
@ -100,7 +100,7 @@ impl ToifImage {
}
}
fn draw_rgb(&self, canvas: &mut dyn Canvas, cache: &DrawingCache) {
fn draw_rgb(&self, canvas: &mut dyn Canvas, cache: &DrawingCache<'a>) {
// TODO: introduce new viewport/shape function for this calculation
let bounds = self.bounds(cache);
let viewport = canvas.viewport();
@ -145,17 +145,17 @@ impl ToifImage {
}
}
impl Shape for ToifImage {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
impl<'a> Shape<'a> for ToifImage<'a> {
fn bounds(&self, _cache: &DrawingCache<'a>) -> Rect {
let size = Offset::new(self.toif.width(), self.toif.height());
Rect::from_top_left_and_size(size.snap(self.pos, self.align), size)
}
fn cleanup(&mut self, _cache: &DrawingCache) {
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) {
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache<'a>) {
if self.toif.is_grayscale() {
self.draw_grayscale(canvas, cache);
} else {
@ -164,8 +164,8 @@ impl Shape for ToifImage {
}
}
impl ShapeClone for ToifImage {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape>
impl<'a> ShapeClone<'a> for ToifImage<'a> {
fn clone_at_bump<'alloc, T>(self, bump: &'alloc T) -> Option<&'alloc mut dyn Shape<'a>>
where
T: LocalAllocLeakExt<'alloc>,
{