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:
parent
2388a8edea
commit
dcda5e0142
@ -10,7 +10,7 @@ use crate::{
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Image {
|
||||
pub toif: Toif,
|
||||
pub toif: Toif<'static>,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user