1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-03 11:20:59 +00:00

refactor(core): make imagebuffer allocation fallible

This commit is contained in:
matejcik 2025-01-02 12:38:49 +01:00 committed by matejcik
parent 83a96f7a5a
commit e424fd8d3b
8 changed files with 48 additions and 39 deletions

View File

@ -1,11 +1,11 @@
use crate::{ use crate::{
error::Error,
io::BinaryData, io::BinaryData,
ui::{ ui::{
component::{Component, Event, EventCtx, Never}, component::{Component, Event, EventCtx, Never},
display::image::ImageInfo, display::image::ImageInfo,
geometry::{Offset, Point, Rect}, geometry::{Offset, Point, Rect},
shape, shape::{self, render_on_canvas, ImageBuffer, Renderer, Rgb565Canvas},
shape::{render_on_canvas, ImageBuffer, Renderer, Rgb565Canvas},
}, },
}; };
@ -17,7 +17,7 @@ pub struct CachedJpeg {
} }
impl CachedJpeg { impl CachedJpeg {
pub fn new(image: BinaryData<'static>, scale: u8) -> Self { pub fn new(image: BinaryData<'static>, scale: u8) -> Result<Self, Error> {
let size = match ImageInfo::parse(image) { let size = match ImageInfo::parse(image) {
ImageInfo::Jpeg(info) => { ImageInfo::Jpeg(info) => {
if info.mcu_height() > 16 { if info.mcu_height() > 16 {
@ -29,7 +29,7 @@ impl CachedJpeg {
_ => Offset::zero(), _ => Offset::zero(),
}; };
let mut buf = unwrap!(ImageBuffer::new(size), "no image buf"); let mut buf = ImageBuffer::new(size)?;
render_on_canvas(buf.canvas(), None, |target| { render_on_canvas(buf.canvas(), None, |target| {
shape::JpegImage::new_image(Point::zero(), image) shape::JpegImage::new_image(Point::zero(), image)
@ -37,12 +37,12 @@ impl CachedJpeg {
.render(target); .render(target);
}); });
Self { Ok(Self {
area: Rect::zero(), area: Rect::zero(),
image_size: size, image_size: size,
jpeg: buf, jpeg: buf,
scale, scale,
} })
} }
} }

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
error::Error,
io::BinaryData, io::BinaryData,
strutil::TString, strutil::TString,
time::{Duration, Instant, Stopwatch}, time::{Duration, Instant, Stopwatch},
@ -432,12 +433,12 @@ impl Homescreen {
label: TString<'static>, label: TString<'static>,
notification: Option<(TString<'static>, u8)>, notification: Option<(TString<'static>, u8)>,
hold_to_lock: bool, hold_to_lock: bool,
) -> Self { ) -> Result<Self, Error> {
let label_width = label.map(|t| theme::TEXT_DEMIBOLD.text_font.text_width(t)); let label_width = label.map(|t| theme::TEXT_DEMIBOLD.text_font.text_width(t));
let label_height = label.map(|t| theme::TEXT_DEMIBOLD.text_font.visible_text_height(t)); let label_height = label.map(|t| theme::TEXT_DEMIBOLD.text_font.visible_text_height(t));
let image = get_homescreen_image(); let image = get_homescreen_image();
let mut buf = unwrap!(ImageBuffer::new(AREA.size()), "no image buf"); let mut buf = ImageBuffer::new(AREA.size())?;
render_on_canvas(buf.canvas(), None, |target| { render_on_canvas(buf.canvas(), None, |target| {
if let Some(image) = image { if let Some(image) = image {
@ -447,7 +448,7 @@ impl Homescreen {
} }
}); });
Self { Ok(Self {
label: Label::new(label, Alignment::Center, theme::TEXT_DEMIBOLD).vertically_centered(), label: Label::new(label, Alignment::Center, theme::TEXT_DEMIBOLD).vertically_centered(),
label_width, label_width,
label_height, label_height,
@ -459,7 +460,7 @@ impl Homescreen {
delay: Timer::new(), delay: Timer::new(),
attach_animation: AttachAnimation::default(), attach_animation: AttachAnimation::default(),
label_anim: HideLabelAnimation::new(label_width), label_anim: HideLabelAnimation::new(label_width),
} })
} }
fn level_to_style(level: u8) -> (Color, Color) { fn level_to_style(level: u8) -> (Color, Color) {
@ -724,9 +725,13 @@ pub struct Lockscreen {
} }
impl Lockscreen { impl Lockscreen {
pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self { pub fn new(
label: TString<'static>,
bootscreen: bool,
coinjoin_authorized: bool,
) -> Result<Self, Error> {
let image = get_homescreen_image(); let image = get_homescreen_image();
let mut buf = unwrap!(ImageBuffer::new(AREA.size()), "no image buf"); let mut buf = ImageBuffer::new(AREA.size())?;
render_on_canvas(buf.canvas(), None, |target| { render_on_canvas(buf.canvas(), None, |target| {
if let Some(image) = image { if let Some(image) = image {
@ -748,7 +753,7 @@ impl Lockscreen {
let label_height = label.map(|t| theme::TEXT_DEMIBOLD.text_font.visible_text_height(t)); let label_height = label.map(|t| theme::TEXT_DEMIBOLD.text_font.visible_text_height(t));
Lockscreen { Ok(Self {
anim: LockscreenAnim::default(), anim: LockscreenAnim::default(),
attach_animation: AttachAnimation::default(), attach_animation: AttachAnimation::default(),
label: Label::new(label, Alignment::Center, theme::TEXT_DEMIBOLD), label: Label::new(label, Alignment::Center, theme::TEXT_DEMIBOLD),
@ -760,7 +765,7 @@ impl Lockscreen {
coinjoin_authorized, coinjoin_authorized,
bg_image: buf, bg_image: buf,
label_anim: HideLabelAnimation::new(label_width), label_anim: HideLabelAnimation::new(label_width),
} })
} }
} }

View File

@ -182,7 +182,7 @@ impl FirmwareUI for UIMercury {
return Err(value_error!(c"Invalid image.")); return Err(value_error!(c"Invalid image."));
}; };
flow::confirm_homescreen::new_confirm_homescreen(title, CachedJpeg::new(image, 1))? flow::confirm_homescreen::new_confirm_homescreen(title, CachedJpeg::new(image, 1)?)?
}; };
Ok(layout) Ok(layout)
} }
@ -904,7 +904,7 @@ impl FirmwareUI for UIMercury {
notification_level: u8, notification_level: u8,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let notification = notification.map(|w| (w, notification_level)); let notification = notification.map(|w| (w, notification_level));
let layout = RootComponent::new(Homescreen::new(label, notification, hold)); let layout = RootComponent::new(Homescreen::new(label, notification, hold)?);
Ok(layout) Ok(layout)
} }
@ -958,7 +958,7 @@ impl FirmwareUI for UIMercury {
bootscreen: bool, bootscreen: bool,
coinjoin_authorized: bool, coinjoin_authorized: bool,
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let layout = RootComponent::new(Lockscreen::new(label, bootscreen, coinjoin_authorized)); let layout = RootComponent::new(Lockscreen::new(label, bootscreen, coinjoin_authorized)?);
Ok(layout) Ok(layout)
} }

View File

@ -1,4 +1,4 @@
use crate::trezorhal::bitblt; use crate::{error::Error, trezorhal::bitblt};
use crate::ui::{display::Color, geometry::Offset}; use crate::ui::{display::Color, geometry::Offset};
@ -61,10 +61,8 @@ impl<'a> Bitmap<'a> {
mut size: Offset, mut size: Offset,
min_height: Option<i16>, min_height: Option<i16>,
buff: &'a [u8], buff: &'a [u8],
) -> Option<Self> { ) -> Result<Self, Error> {
if size.x < 0 && size.y < 0 { assert!(size.x >= 0 && size.y >= 0);
return None;
}
let min_stride = match format { let min_stride = match format {
BitmapFormat::MONO1 => (size.x + 7) / 8, BitmapFormat::MONO1 => (size.x + 7) / 8,
@ -101,14 +99,14 @@ impl<'a> Bitmap<'a> {
if max_height >= min_height as usize { if max_height >= min_height as usize {
size.y = max_height as i16; size.y = max_height as i16;
} else { } else {
return None; return Err(Error::ValueError(c"Buffer too small."));
} }
} else { } else {
return None; return Err(Error::ValueError(c"Buffer too small."));
} }
} }
Some(Self { Ok(Self {
ptr: buff.as_ptr() as *mut u8, ptr: buff.as_ptr() as *mut u8,
stride, stride,
size, size,
@ -134,10 +132,10 @@ impl<'a> Bitmap<'a> {
size: Offset, size: Offset,
min_height: Option<i16>, min_height: Option<i16>,
buff: &'a mut [u8], buff: &'a mut [u8],
) -> Option<Self> { ) -> Result<Self, Error> {
let mut bitmap = Self::new(format, stride, size, min_height, buff)?; let mut bitmap = Self::new(format, stride, size, min_height, buff)?;
bitmap.mutable = true; bitmap.mutable = true;
Some(bitmap) Ok(bitmap)
} }
/// Returns bitmap width in pixels. /// Returns bitmap width in pixels.

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
error::Error,
trezorhal::bitblt::{BitBltCopy, BitBltFill}, trezorhal::bitblt::{BitBltCopy, BitBltFill},
ui::{ ui::{
display::Color, display::Color,
@ -32,10 +33,10 @@ impl<'a> Mono8Canvas<'a> {
stride: Option<usize>, stride: Option<usize>,
min_height: Option<i16>, min_height: Option<i16>,
buff: &'a mut [u8], buff: &'a mut [u8],
) -> Option<Self> { ) -> Result<Self, Error> {
let bitmap = Bitmap::new_mut(BitmapFormat::MONO8, stride, size, min_height, buff)?; let bitmap = Bitmap::new_mut(BitmapFormat::MONO8, stride, size, min_height, buff)?;
let viewport = Viewport::from_size(bitmap.size()); let viewport = Viewport::from_size(bitmap.size());
Some(Self { bitmap, viewport }) Ok(Self { bitmap, viewport })
} }
/// Returns the specified row as a mutable slice. /// Returns the specified row as a mutable slice.

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
error::Error,
trezorhal::bitblt::{BitBltCopy, BitBltFill}, trezorhal::bitblt::{BitBltCopy, BitBltFill},
ui::{ ui::{
display::Color, display::Color,
@ -32,10 +33,10 @@ impl<'a> Rgb565Canvas<'a> {
stride: Option<usize>, stride: Option<usize>,
min_height: Option<i16>, min_height: Option<i16>,
buff: &'a mut [u8], buff: &'a mut [u8],
) -> Option<Self> { ) -> Result<Self, Error> {
let bitmap = Bitmap::new_mut(BitmapFormat::RGB565, stride, size, min_height, buff)?; let bitmap = Bitmap::new_mut(BitmapFormat::RGB565, stride, size, min_height, buff)?;
let viewport = Viewport::from_size(bitmap.size()); let viewport = Viewport::from_size(bitmap.size());
Some(Self { bitmap, viewport }) Ok(Self { bitmap, viewport })
} }
/// Returns the specified row as a mutable slice. /// Returns the specified row as a mutable slice.

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
error::Error,
trezorhal::bitblt::{BitBltCopy, BitBltFill}, trezorhal::bitblt::{BitBltCopy, BitBltFill},
ui::{ ui::{
display::Color, display::Color,
@ -32,10 +33,10 @@ impl<'a> Rgba8888Canvas<'a> {
stride: Option<usize>, stride: Option<usize>,
min_height: Option<i16>, min_height: Option<i16>,
buff: &'a mut [u8], buff: &'a mut [u8],
) -> Option<Self> { ) -> Result<Self, Error> {
let bitmap = Bitmap::new_mut(BitmapFormat::RGBA8888, stride, size, min_height, buff)?; let bitmap = Bitmap::new_mut(BitmapFormat::RGBA8888, stride, size, min_height, buff)?;
let viewport = Viewport::from_size(bitmap.size()); let viewport = Viewport::from_size(bitmap.size());
Some(Self { bitmap, viewport }) Ok(Self { bitmap, viewport })
} }
/// Returns the specified row as a mutable slice. /// Returns the specified row as a mutable slice.

View File

@ -1,6 +1,9 @@
use crate::ui::{ use crate::{
geometry::Offset, error::Error,
shape::{Bitmap, BitmapView, Canvas, CanvasBuilder}, ui::{
geometry::Offset,
shape::{Bitmap, BitmapView, Canvas, CanvasBuilder},
},
}; };
/// Size of image buffer in bytes /// Size of image buffer in bytes
@ -48,7 +51,7 @@ where
/// ///
/// Returns `None` if the buffer is already in use or the /// Returns `None` if the buffer is already in use or the
/// buffer is not big enough to hold the image. /// buffer is not big enough to hold the image.
pub fn new(size: Offset) -> Option<Self> { pub fn new(size: Offset) -> Result<Self, Error> {
// SAFETY: // SAFETY:
// It's safe to read/modify mutable static variable as // It's safe to read/modify mutable static variable as
// whole app is single-threaded. // whole app is single-threaded.
@ -58,7 +61,7 @@ where
// to prevent that. // to prevent that.
unsafe { unsafe {
if IMAGE_BUFFER_LOCKED { if IMAGE_BUFFER_LOCKED {
return None; return Err(Error::AllocationFailed);
} }
let bitmap = let bitmap =
@ -66,7 +69,7 @@ where
IMAGE_BUFFER_LOCKED = true; IMAGE_BUFFER_LOCKED = true;
Some(Self { Ok(Self {
canvas: T::from_bitmap(bitmap), canvas: T::from_bitmap(bitmap),
}) })
} }