1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-23 06:48:16 +00:00

feat(core): support old homescreen format

[no changelog]
This commit is contained in:
tychovrahe 2023-03-21 16:49:37 +01:00 committed by TychoVrahe
parent 2388a8edea
commit dcda5e0142
4 changed files with 204 additions and 65 deletions

View File

@ -10,7 +10,7 @@ use crate::{
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Image {
pub toif: Toif,
pub toif: Toif<'static>,
area: Rect,
}

View File

@ -52,12 +52,12 @@ pub fn icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color) {
/// Holding toif data and allowing it to draw itself.
/// See https://docs.trezor.io/trezor-firmware/misc/toif.html for data format.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Toif {
data: &'static [u8],
pub struct Toif<'i> {
data: &'i [u8],
}
impl Toif {
pub const fn new(data: &'static [u8]) -> Option<Self> {
impl<'i> Toif<'i> {
pub const fn new(data: &'i [u8]) -> Option<Self> {
if data.len() < TOIF_HEADER_LENGTH || data[0] != b'T' || data[1] != b'O' || data[2] != b'I'
{
return None;
@ -91,7 +91,7 @@ impl Toif {
Offset::new(self.width(), self.height())
}
pub fn zdata(&self) -> &'static [u8] {
pub fn zdata(&self) -> &'i [u8] {
&self.data[TOIF_HEADER_LENGTH..]
}
@ -110,7 +110,7 @@ impl Toif {
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Icon {
pub toif: Toif,
pub toif: Toif<'static>,
}
impl Icon {

View File

@ -14,6 +14,15 @@ use crate::{
},
};
use crate::{
trezorhal::{display::ToifFormat, uzlib::UZLIB_WINDOW_SIZE},
ui::{
display::{tjpgd::BufferInput, toif::Toif},
model_tt::component::homescreen::render::{
HomescreenJpeg, HomescreenToif, HOMESCREEN_TOIF_SIZE,
},
},
};
use render::{
homescreen, homescreen_blurred, HomescreenNotification, HomescreenText, HOMESCREEN_IMAGE_SIZE,
};
@ -195,16 +204,39 @@ where
let notification = self.get_notification();
let res = get_image();
let mut show_default = true;
if let Ok(data) = res {
if is_image_jpeg(data.as_ref()) {
let mut input = BufferInput(data.as_ref());
let mut hs_img = HomescreenJpeg::new(&mut input);
homescreen(
data.as_ref(),
&mut hs_img,
&[text],
notification,
self.paint_notification_only,
);
} else {
show_default = false;
} else if is_image_toif(data.as_ref()) {
let input = unwrap!(Toif::new(data.as_ref()));
let mut window = [0; UZLIB_WINDOW_SIZE];
let mut hs_img =
HomescreenToif::new(input.decompression_context(Some(&mut window)));
homescreen(
IMAGE_HOMESCREEN,
&mut hs_img,
&[text],
notification,
self.paint_notification_only,
);
show_default = false;
}
}
if show_default {
let mut input = BufferInput(IMAGE_HOMESCREEN);
let mut hs_img = HomescreenJpeg::new(&mut input);
homescreen(
&mut hs_img,
&[text],
notification,
self.paint_notification_only,
@ -289,10 +321,28 @@ where
];
let res = get_image();
let mut show_default = true;
if let Ok(data) = res {
homescreen_blurred(data.as_ref(), &texts);
} else {
homescreen_blurred(IMAGE_HOMESCREEN, &texts);
if is_image_jpeg(data.as_ref()) {
let mut input = BufferInput(data.as_ref());
let mut hs_img = HomescreenJpeg::new(&mut input);
homescreen_blurred(&mut hs_img, &texts);
show_default = false;
} else if is_image_toif(data.as_ref()) {
let input = unwrap!(Toif::new(data.as_ref()));
let mut window = [0; UZLIB_WINDOW_SIZE];
let mut hs_img =
HomescreenToif::new(input.decompression_context(Some(&mut window)));
homescreen_blurred(&mut hs_img, &texts);
show_default = false;
}
}
if show_default {
let mut input = BufferInput(IMAGE_HOMESCREEN);
let mut hs_img = HomescreenJpeg::new(&mut input);
homescreen_blurred(&mut hs_img, &texts);
}
}
}
@ -303,21 +353,36 @@ fn get_image() -> Result<Gc<[u8]>, ()> {
if let Ok(mut buffer) = result {
let buf = unsafe { Gc::<[u8]>::as_mut(&mut buffer) };
if get_avatar(buf).is_ok() {
let jpeg = jpeg_info(buffer.as_ref());
if let Some((size, mcu_height)) = jpeg {
if size.x == HOMESCREEN_IMAGE_SIZE
&& size.y == HOMESCREEN_IMAGE_SIZE
&& mcu_height <= 16
{
return Ok(buffer);
}
}
}
}
};
Err(())
}
fn is_image_jpeg(buffer: &[u8]) -> bool {
let jpeg = jpeg_info(buffer);
if let Some((size, mcu_height)) = jpeg {
if size.x == HOMESCREEN_IMAGE_SIZE && size.y == HOMESCREEN_IMAGE_SIZE && mcu_height <= 16 {
return true;
}
}
false
}
fn is_image_toif(buffer: &[u8]) -> bool {
let toif = Toif::new(buffer);
if let Some(toif) = toif {
if toif.size().x == HOMESCREEN_TOIF_SIZE
&& toif.size().y == HOMESCREEN_TOIF_SIZE
&& toif.format() == ToifFormat::FullColorBE
{
return true;
}
}
false
}
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Lockscreen<T> {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {

View File

@ -8,6 +8,7 @@ use crate::{
buffers::{get_blurring_buffer, get_jpeg_buffer, get_jpeg_work_buffer, BufferJpeg},
display,
display::bar_radius_buffer,
uzlib::UzlibContext,
},
ui::{
constant::screen,
@ -51,6 +52,9 @@ struct HomescreenTextInfo {
}
pub const HOMESCREEN_IMAGE_SIZE: i16 = 240;
pub const HOMESCREEN_TOIF_SIZE: i16 = 144;
pub const HOMESCREEN_TOIF_Y_OFFSET: i16 = 27;
pub const HOMESCREEN_TOIF_X_OFFSET: usize = ((WIDTH - HOMESCREEN_TOIF_SIZE) / 2) as usize;
const HOMESCREEN_MAX_ICON_SIZE: i16 = 20;
const NOTIFICATION_HEIGHT: i16 = 36;
@ -79,6 +83,102 @@ const RED_IDX: usize = 0;
const GREEN_IDX: usize = 1;
const BLUE_IDX: usize = 2;
pub trait HomescreenDecompressor {
fn get_height(&self) -> i16;
fn decompress(&mut self);
fn get_data(&mut self) -> &mut BufferJpeg;
}
pub struct HomescreenJpeg<'i> {
pub output: BufferOutput<'i>,
pub jdec: Option<JDEC<'i, 'i>>,
}
impl<'i> HomescreenJpeg<'i> {
pub fn new(input: &'i mut BufferInput<'i>) -> Self {
Self {
output: BufferOutput::new(unsafe { get_jpeg_buffer(0, true) }, WIDTH, 16),
jdec: JDEC::new(input, unsafe {
get_jpeg_work_buffer(0, true).buffer.as_mut_slice()
})
.ok(),
}
}
}
impl<'i> HomescreenDecompressor for HomescreenJpeg<'i> {
fn get_height(&self) -> i16 {
if let Some(dec) = self.jdec.as_ref() {
return dec.mcu_height();
}
1
}
fn decompress(&mut self) {
self.jdec.as_mut().map(|dec| dec.decomp(&mut self.output));
}
fn get_data(&mut self) -> &mut BufferJpeg {
self.output.buffer()
}
}
pub struct HomescreenToif<'i> {
pub output: BufferOutput<'i>,
pub decomp_context: UzlibContext<'i>,
line: i16,
}
impl<'i> HomescreenToif<'i> {
pub fn new(context: UzlibContext<'i>) -> Self {
Self {
output: BufferOutput::new(unsafe { get_jpeg_buffer(0, true) }, WIDTH, 16),
decomp_context: context,
line: 0,
}
}
}
impl<'i> HomescreenDecompressor for HomescreenToif<'i> {
fn get_height(&self) -> i16 {
1
}
fn decompress(&mut self) {
// SAFETY: Aligning to u8 slice is safe, because the original slice is aligned
// to 16 bits, therefore there are also no residuals (prefix/suffix).
// The data in the slices are integers, so these are valid for both u16
// and u8.
if self.line >= HOMESCREEN_TOIF_Y_OFFSET
&& self.line < HOMESCREEN_TOIF_Y_OFFSET + HOMESCREEN_TOIF_SIZE
{
let (_, workbuf, _) = unsafe { self.output.buffer().buffer.align_to_mut::<u8>() };
let result = self.decomp_context.uncompress(
&mut workbuf[2 * HOMESCREEN_TOIF_X_OFFSET
..2 * HOMESCREEN_TOIF_X_OFFSET + 2 * HOMESCREEN_TOIF_SIZE as usize],
);
if result.is_err() {
self.output.buffer().buffer.fill(0);
} else {
for i in 0..HOMESCREEN_TOIF_SIZE as usize {
workbuf.swap(
2 * HOMESCREEN_TOIF_X_OFFSET + 2 * i,
2 * HOMESCREEN_TOIF_X_OFFSET + 2 * i + 1,
);
}
}
} else {
self.output.buffer().buffer.fill(0);
}
self.line += 1;
}
fn get_data(&mut self) -> &mut BufferJpeg {
self.output.buffer()
}
}
fn homescreen_get_fg_text(
y_tmp: i16,
text_info: HomescreenTextInfo,
@ -428,7 +528,7 @@ fn get_data(buffer: &mut BufferJpeg, line_num: i16, mcu_height: i16) -> &mut [u1
&mut buffer.buffer[data_start..data_end]
}
pub fn homescreen_blurred(data: &[u8], texts: &[HomescreenText]) {
pub fn homescreen_blurred(data: &mut dyn HomescreenDecompressor, texts: &[HomescreenText]) {
let mut icon_data = [0_u8; (HOMESCREEN_MAX_ICON_SIZE * HOMESCREEN_MAX_ICON_SIZE / 2) as usize];
let text_buffer = unsafe { get_text_buffer(0, true) };
@ -437,27 +537,15 @@ pub fn homescreen_blurred(data: &[u8], texts: &[HomescreenText]) {
let mut text_info =
homescreen_position_text(unwrap!(texts.get(0)), text_buffer, &mut icon_data);
let jpeg_pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() };
let jpeg_buffer = unsafe { get_jpeg_buffer(0, true) };
let mut jpeg_input = BufferInput(data);
let mut jpeg_output = BufferOutput::new(jpeg_buffer, WIDTH, 16);
let mut mcu_height = 8;
let mut jd: Option<JDEC> = JDEC::new(&mut jpeg_input, jpeg_pool).ok();
if let Some(dec) = &jd {
mcu_height = dec.mcu_height();
if !(dec.width() == WIDTH && dec.height() == HEIGHT && mcu_height <= 16) {
jd = None
}
}
jd.as_mut().map(|dec| dec.decomp(&mut jpeg_output));
let mcu_height = data.get_height();
data.decompress();
set_window(screen());
let mut blurring = BlurringContext::new();
// handling top edge case: preload the edge value N+1 times
blurring.compute_line_avgs(jpeg_output.buffer(), mcu_height);
blurring.compute_line_avgs(data.get_data(), mcu_height);
for _ in 0..=BLUR_RADIUS {
blurring.vertical_avg_add();
@ -466,12 +554,12 @@ pub fn homescreen_blurred(data: &[u8], texts: &[HomescreenText]) {
// load enough values to be able to compute first line averages
for _ in 0..BLUR_RADIUS {
blurring.compute_line_avgs(jpeg_output.buffer(), mcu_height);
blurring.compute_line_avgs(data.get_data(), mcu_height);
blurring.vertical_avg_add();
blurring.inc_add();
if (blurring.get_line_num() % mcu_height) == 0 {
jd.as_mut().map(|dec| dec.decomp(&mut jpeg_output));
data.decompress();
}
}
@ -479,7 +567,7 @@ pub fn homescreen_blurred(data: &[u8], texts: &[HomescreenText]) {
// several lines have been already decompressed before this loop, adjust for
// that
if y < HOMESCREEN_IMAGE_SIZE - (BLUR_RADIUS + 1) {
blurring.compute_line_avgs(jpeg_output.buffer(), mcu_height);
blurring.compute_line_avgs(data.get_data(), mcu_height);
}
let done = homescreen_line_blurred(&icon_data, text_buffer, text_info, &blurring, y);
@ -510,14 +598,14 @@ pub fn homescreen_blurred(data: &[u8], texts: &[HomescreenText]) {
}
if (blurring.get_line_num() % mcu_height) == 0 && (blurring.get_line_num() < HEIGHT) {
jd.as_mut().map(|dec| dec.decomp(&mut jpeg_output));
data.decompress();
}
}
dma2d_wait_for_transfer();
}
pub fn homescreen(
data: &[u8],
data: &mut dyn HomescreenDecompressor,
texts: &[HomescreenText],
notification: Option<HomescreenNotification>,
notification_only: bool,
@ -551,34 +639,20 @@ pub fn homescreen(
homescreen_position_text(unwrap!(texts.get(0)), text_buffer, &mut icon_data)
};
let jpeg_pool = unsafe { get_jpeg_work_buffer(0, true).buffer.as_mut_slice() };
let jpeg_buffer = unsafe { get_jpeg_buffer(0, true) };
let mut jpeg_input = BufferInput(data);
let mut jpeg_output = BufferOutput::new(jpeg_buffer, WIDTH, 16);
let mut mcu_height = 8;
let mut jd: Option<JDEC> = JDEC::new(&mut jpeg_input, jpeg_pool).ok();
if let Some(dec) = &jd {
mcu_height = dec.mcu_height();
if !(dec.width() == WIDTH && dec.height() == HEIGHT && mcu_height <= 16) {
jd = None
}
}
set_window(screen());
let mcu_height = mcu_height as i16;
let mcu_height = data.get_height();
for y in 0..HEIGHT {
if (y % mcu_height) == 0 {
jd.as_mut().map(|dec| dec.decomp(&mut jpeg_output));
data.decompress();
}
let done = homescreen_line(
&icon_data,
text_buffer,
text_info,
jpeg_output.buffer(),
data.get_data(),
mcu_height,
y,
);