mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 22:38:08 +00:00
refactor(core): remove old homescreen rendering from mercury UI
[no changelog]
This commit is contained in:
parent
0993ca0434
commit
3109001fa0
@ -1,5 +1,3 @@
|
||||
mod render;
|
||||
|
||||
use crate::{
|
||||
micropython::gc::Gc,
|
||||
strutil::TString,
|
||||
@ -17,26 +15,12 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
trezorhal::{buffers::BufferJpegWork, uzlib::UZLIB_WINDOW_SIZE},
|
||||
ui::{
|
||||
constant::{screen, HEIGHT},
|
||||
display::{
|
||||
tjpgd::BufferInput,
|
||||
toif::{Toif, ToifFormat},
|
||||
},
|
||||
model_mercury::{
|
||||
component::homescreen::render::{HomescreenJpeg, HomescreenToif, HOMESCREEN_TOIF_SIZE},
|
||||
theme::{
|
||||
GREEN_LIGHT, GREY_LIGHT, ICON_CENTRAL_CIRCLE, ICON_KEY, ICON_LOCKSCREEN_FILTER,
|
||||
},
|
||||
},
|
||||
use crate::ui::{
|
||||
constant::{screen, HEIGHT, WIDTH},
|
||||
model_mercury::theme::{
|
||||
GREEN_LIGHT, GREY_LIGHT, ICON_CENTRAL_CIRCLE, ICON_KEY, ICON_LOCKSCREEN_FILTER,
|
||||
},
|
||||
};
|
||||
use render::{
|
||||
homescreen, homescreen_blurred, HomescreenNotification, HomescreenText,
|
||||
HOMESCREEN_IMAGE_HEIGHT, HOMESCREEN_IMAGE_WIDTH,
|
||||
};
|
||||
|
||||
use super::{theme, Loader, LoaderMsg};
|
||||
|
||||
@ -51,6 +35,16 @@ const LOADER_OFFSET: Offset = Offset::y(-10);
|
||||
const LOADER_DELAY: Duration = Duration::from_millis(500);
|
||||
const LOADER_DURATION: Duration = Duration::from_millis(2000);
|
||||
|
||||
pub const HOMESCREEN_IMAGE_WIDTH: i16 = WIDTH;
|
||||
pub const HOMESCREEN_IMAGE_HEIGHT: i16 = HEIGHT;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HomescreenNotification {
|
||||
pub text: TString<'static>,
|
||||
pub icon: Icon,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
pub struct Homescreen {
|
||||
label: TString<'static>,
|
||||
notification: Option<(TString<'static>, u8)>,
|
||||
@ -109,19 +103,6 @@ impl Homescreen {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_loader(&mut self) {
|
||||
TR::progress__locking_device.map_translated(|t| {
|
||||
display::text_center(
|
||||
TOP_CENTER + Offset::y(HOLD_Y),
|
||||
t,
|
||||
Font::NORMAL,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
)
|
||||
});
|
||||
self.loader.paint()
|
||||
}
|
||||
|
||||
fn render_loader<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
TR::progress__locking_device.map_translated(|t| {
|
||||
shape::Text::new(TOP_CENTER + Offset::y(HOLD_Y), t)
|
||||
@ -197,63 +178,7 @@ impl Component for Homescreen {
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.pad.paint();
|
||||
if self.loader.is_animating() || self.loader.is_completely_grown(Instant::now()) {
|
||||
self.paint_loader();
|
||||
} else {
|
||||
let mut label_style = theme::TEXT_DEMIBOLD;
|
||||
label_style.text_color = theme::FG;
|
||||
|
||||
let text = HomescreenText {
|
||||
text: self.label,
|
||||
style: label_style,
|
||||
offset: Offset::y(LABEL_Y),
|
||||
icon: None,
|
||||
};
|
||||
|
||||
let notification = self.get_notification();
|
||||
|
||||
let mut show_default = true;
|
||||
|
||||
if let Some(ref data) = self.custom_image {
|
||||
if is_image_jpeg(data.as_ref()) {
|
||||
let input = BufferInput(data.as_ref());
|
||||
let mut pool = BufferJpegWork::get_cleared();
|
||||
let mut hs_img = HomescreenJpeg::new(input, pool.buffer.as_mut_slice());
|
||||
homescreen(
|
||||
&mut hs_img,
|
||||
&[text],
|
||||
notification,
|
||||
self.paint_notification_only,
|
||||
);
|
||||
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(
|
||||
&mut hs_img,
|
||||
&[text],
|
||||
notification,
|
||||
self.paint_notification_only,
|
||||
);
|
||||
show_default = false;
|
||||
}
|
||||
}
|
||||
|
||||
if show_default {
|
||||
let input = BufferInput(IMAGE_HOMESCREEN);
|
||||
let mut pool = BufferJpegWork::get_cleared();
|
||||
let mut hs_img = HomescreenJpeg::new(input, pool.buffer.as_mut_slice());
|
||||
homescreen(
|
||||
&mut hs_img,
|
||||
&[text],
|
||||
notification,
|
||||
self.paint_notification_only,
|
||||
);
|
||||
}
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
@ -269,10 +194,6 @@ impl Component for Homescreen {
|
||||
shape::JpegImage::new(AREA.center(), img_data)
|
||||
.with_align(Alignment2D::CENTER)
|
||||
.render(target);
|
||||
} else if is_image_toif(img_data) {
|
||||
shape::ToifImage::new(self.pad.area.center(), unwrap!(Toif::new(img_data)))
|
||||
.with_align(Alignment2D::CENTER)
|
||||
.render(target);
|
||||
}
|
||||
|
||||
self.label.map(|t| {
|
||||
@ -381,74 +302,7 @@ impl Component for Lockscreen<'_> {
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
let (locked, tap) = if self.bootscreen {
|
||||
(
|
||||
TR::lockscreen__title_not_connected,
|
||||
TR::lockscreen__tap_to_connect,
|
||||
)
|
||||
} else {
|
||||
(TR::lockscreen__title_locked, TR::lockscreen__tap_to_unlock)
|
||||
};
|
||||
|
||||
let mut label_style = theme::TEXT_DEMIBOLD;
|
||||
label_style.text_color = theme::GREY_LIGHT;
|
||||
|
||||
let mut texts: &[HomescreenText] = &[
|
||||
HomescreenText {
|
||||
text: "".into(),
|
||||
style: theme::TEXT_NORMAL,
|
||||
offset: Offset::new(2, COINJOIN_Y),
|
||||
icon: Some(theme::ICON_COINJOIN),
|
||||
},
|
||||
HomescreenText {
|
||||
text: locked.into(),
|
||||
style: theme::TEXT_BOLD,
|
||||
offset: Offset::y(LOCKED_Y),
|
||||
icon: Some(theme::ICON_LOCK),
|
||||
},
|
||||
HomescreenText {
|
||||
text: tap.into(),
|
||||
style: theme::TEXT_NORMAL,
|
||||
offset: Offset::y(TAP_Y),
|
||||
icon: None,
|
||||
},
|
||||
HomescreenText {
|
||||
text: self.label,
|
||||
style: label_style,
|
||||
offset: Offset::y(LABEL_Y),
|
||||
icon: None,
|
||||
},
|
||||
];
|
||||
|
||||
if !self.coinjoin_authorized {
|
||||
texts = &texts[1..];
|
||||
}
|
||||
|
||||
let mut show_default = true;
|
||||
|
||||
if let Some(ref data) = self.custom_image {
|
||||
if is_image_jpeg(data.as_ref()) {
|
||||
let input = BufferInput(data.as_ref());
|
||||
let mut pool = BufferJpegWork::get_cleared();
|
||||
let mut hs_img = HomescreenJpeg::new(input, pool.buffer.as_mut_slice());
|
||||
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 input = BufferInput(IMAGE_HOMESCREEN);
|
||||
let mut pool = BufferJpegWork::get_cleared();
|
||||
let mut hs_img = HomescreenJpeg::new(input, pool.buffer.as_mut_slice());
|
||||
homescreen_blurred(&mut hs_img, texts);
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
@ -560,19 +414,6 @@ fn is_image_jpeg(buffer: &[u8]) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_image_toif(buffer: &[u8]) -> bool {
|
||||
let toif = Toif::new(buffer);
|
||||
if let Ok(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 crate::trace::Trace for Lockscreen<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -1,768 +0,0 @@
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
trezorhal::{
|
||||
buffers::{
|
||||
BufferBlurring, BufferBlurringTotals, BufferJpeg, BufferLine16bpp, BufferLine4bpp,
|
||||
BufferText,
|
||||
},
|
||||
display,
|
||||
dma2d::{dma2d_setup_4bpp_over_16bpp, dma2d_start_blend, dma2d_wait_for_transfer},
|
||||
uzlib::UzlibContext,
|
||||
},
|
||||
ui::{
|
||||
component::text::TextStyle,
|
||||
constant::{screen, HEIGHT, WIDTH},
|
||||
display::{
|
||||
position_buffer, rect_fill_rounded_buffer, set_window,
|
||||
tjpgd::{BufferInput, BufferOutput, JDEC},
|
||||
Color, Icon,
|
||||
},
|
||||
geometry::{Offset, Point, Rect},
|
||||
model_mercury::theme,
|
||||
util::icon_text_center,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HomescreenText<'a> {
|
||||
pub text: TString<'a>,
|
||||
pub style: TextStyle,
|
||||
pub offset: Offset,
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HomescreenNotification {
|
||||
pub text: TString<'static>,
|
||||
pub icon: Icon,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct HomescreenTextInfo {
|
||||
pub text_area: Rect,
|
||||
pub text_width: i16,
|
||||
pub text_color: Color,
|
||||
pub icon_area: Option<Rect>,
|
||||
}
|
||||
|
||||
pub const HOMESCREEN_IMAGE_WIDTH: i16 = WIDTH;
|
||||
pub const HOMESCREEN_IMAGE_HEIGHT: i16 = HEIGHT;
|
||||
pub const HOMESCREEN_TOIF_SIZE: i16 = 144;
|
||||
pub const HOMESCREEN_TOIF_Y_OFFSET: i16 = 27;
|
||||
pub const HOMESCREEN_TOIF_X_OFFSET: usize =
|
||||
((WIDTH.saturating_sub(HOMESCREEN_TOIF_SIZE)) / 2) as usize;
|
||||
|
||||
const HOMESCREEN_MAX_ICON_SIZE: i16 = 20;
|
||||
const NOTIFICATION_HEIGHT: i16 = 36;
|
||||
const NOTIFICATION_BORDER: i16 = 6;
|
||||
const TEXT_ICON_SPACE: i16 = 2;
|
||||
|
||||
const HOMESCREEN_DIM_HEIGHT: i16 = 35;
|
||||
const HOMESCREEN_DIM_START: i16 = HOMESCREEN_IMAGE_HEIGHT - 42;
|
||||
const HOMESCREEN_DIM: f32 = 0.65;
|
||||
const HOMESCREEN_DIM_BORDER: i16 = theme::BUTTON_SPACING;
|
||||
|
||||
const LOCKSCREEN_DIM: f32 = 0.55;
|
||||
const LOCKSCREEN_DIM_BG: f32 = 0.0;
|
||||
const LOCKSCREEN_DIM_ALL: bool = true;
|
||||
|
||||
const BLUR_SIZE: usize = 9;
|
||||
const BLUR_DIV: u32 =
|
||||
((65536_f32 * (1_f32 - LOCKSCREEN_DIM_BG)) as u32) / ((BLUR_SIZE * BLUR_SIZE) as u32);
|
||||
const DECOMP_LINES: usize = BLUR_SIZE + 1;
|
||||
const BLUR_RADIUS: i16 = (BLUR_SIZE / 2) as i16;
|
||||
|
||||
const COLORS: usize = 3;
|
||||
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,
|
||||
pub input: BufferInput<'i>,
|
||||
pub jdec: Option<JDEC<'i>>,
|
||||
}
|
||||
|
||||
impl<'i> HomescreenJpeg<'i> {
|
||||
pub fn new(mut input: BufferInput<'i>, pool: &'i mut [u8]) -> Self {
|
||||
Self {
|
||||
output: BufferOutput::new(WIDTH, 16),
|
||||
jdec: JDEC::new(&mut input, pool).ok(),
|
||||
input,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.input, &mut self.output));
|
||||
}
|
||||
|
||||
fn get_data(&mut self) -> &mut BufferJpeg {
|
||||
self.output.buffer()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HomescreenToif<'i> {
|
||||
pub output: BufferOutput,
|
||||
pub decomp_context: UzlibContext<'i>,
|
||||
line: i16,
|
||||
}
|
||||
|
||||
impl<'i> HomescreenToif<'i> {
|
||||
pub fn new(context: UzlibContext<'i>) -> Self {
|
||||
Self {
|
||||
output: BufferOutput::new(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,
|
||||
text_buffer: &BufferText,
|
||||
fg_buffer: &mut BufferLine4bpp,
|
||||
) -> bool {
|
||||
if y_tmp >= text_info.text_area.y0 && y_tmp < text_info.text_area.y1 {
|
||||
let y_pos = y_tmp - text_info.text_area.y0;
|
||||
position_buffer(
|
||||
&mut fg_buffer.buffer,
|
||||
&text_buffer.buffer[(y_pos * WIDTH / 2) as usize..((y_pos + 1) * WIDTH / 2) as usize],
|
||||
4,
|
||||
text_info.text_area.x0,
|
||||
text_info.text_width,
|
||||
);
|
||||
}
|
||||
|
||||
y_tmp == (text_info.text_area.y1 - 1)
|
||||
}
|
||||
|
||||
fn homescreen_get_fg_icon(
|
||||
y_tmp: i16,
|
||||
text_info: HomescreenTextInfo,
|
||||
icon_data: &[u8],
|
||||
fg_buffer: &mut BufferLine4bpp,
|
||||
) {
|
||||
if let Some(icon_area) = text_info.icon_area {
|
||||
let icon_size = icon_area.size();
|
||||
if y_tmp >= icon_area.y0 && y_tmp < icon_area.y1 {
|
||||
let y_pos = y_tmp - icon_area.y0;
|
||||
position_buffer(
|
||||
&mut fg_buffer.buffer,
|
||||
&icon_data
|
||||
[(y_pos * icon_size.x / 2) as usize..((y_pos + 1) * icon_size.x / 2) as usize],
|
||||
4,
|
||||
icon_area.x0,
|
||||
icon_size.x,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn homescreen_position_text(
|
||||
text: &HomescreenText,
|
||||
buffer: &mut BufferText,
|
||||
icon_buffer: &mut [u8],
|
||||
) -> HomescreenTextInfo {
|
||||
let text_width = text
|
||||
.text
|
||||
.map(|t| display::text_width(t, text.style.text_font.into()));
|
||||
let font_max_height = display::text_max_height(text.style.text_font.into());
|
||||
let font_baseline = display::text_baseline(text.style.text_font.into());
|
||||
let text_width_clamped = text_width.clamp(0, screen().width());
|
||||
|
||||
let icon_size = if let Some(icon) = text.icon {
|
||||
let size = icon.toif.size();
|
||||
assert!(size.x <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
assert!(size.y <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
icon.toif.uncompress(icon_buffer);
|
||||
size
|
||||
} else {
|
||||
Offset::zero()
|
||||
};
|
||||
|
||||
let text_top = screen().y0 + text.offset.y - font_max_height + font_baseline;
|
||||
let text_bottom = screen().y0 + text.offset.y + font_baseline;
|
||||
|
||||
let total_width = text_width_clamped + icon_size.x + TEXT_ICON_SPACE;
|
||||
let icon_left = screen().center().x + text.offset.x - total_width / 2;
|
||||
let text_left = icon_left + icon_size.x + TEXT_ICON_SPACE;
|
||||
let text_right = screen().center().x + text.offset.x + total_width / 2;
|
||||
|
||||
let text_area = Rect::new(
|
||||
Point::new(text_left, text_top),
|
||||
Point::new(text_right, text_bottom),
|
||||
);
|
||||
|
||||
let icon_area = if text.icon.is_some() {
|
||||
Some(Rect::from_top_left_and_size(
|
||||
Point::new(icon_left, text_bottom - icon_size.y - font_baseline),
|
||||
icon_size,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
text.text
|
||||
.map(|t| display::text_into_buffer(t, text.style.text_font.into(), buffer, 0));
|
||||
|
||||
HomescreenTextInfo {
|
||||
text_area,
|
||||
text_width,
|
||||
text_color: text.style.text_color,
|
||||
icon_area,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn homescreen_dim_area(x: i16, y: i16) -> bool {
|
||||
y >= HOMESCREEN_DIM_START
|
||||
&& (y > HOMESCREEN_DIM_START + 1
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT - 1)
|
||||
&& x > HOMESCREEN_DIM_BORDER
|
||||
&& x < WIDTH - HOMESCREEN_DIM_BORDER)
|
||||
|| (y > HOMESCREEN_DIM_START
|
||||
&& y < (HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT)
|
||||
&& x > HOMESCREEN_DIM_BORDER + 1
|
||||
&& x < WIDTH - (HOMESCREEN_DIM_BORDER + 1))
|
||||
|| ((HOMESCREEN_DIM_START..=(HOMESCREEN_DIM_START + HOMESCREEN_DIM_HEIGHT)).contains(&y)
|
||||
&& x > HOMESCREEN_DIM_BORDER + 2
|
||||
&& x < WIDTH - (HOMESCREEN_DIM_BORDER + 2))
|
||||
}
|
||||
|
||||
fn homescreen_line_blurred(
|
||||
icon_data: &[u8],
|
||||
text_buffer: &mut BufferText,
|
||||
fg_buffer: &mut BufferLine4bpp,
|
||||
img_buffer: &mut BufferLine16bpp,
|
||||
text_info: HomescreenTextInfo,
|
||||
blurring: &BlurringContext,
|
||||
y: i16,
|
||||
) -> bool {
|
||||
fg_buffer.buffer.fill(0);
|
||||
for x in 0..HOMESCREEN_IMAGE_WIDTH {
|
||||
let c = if LOCKSCREEN_DIM_ALL {
|
||||
let x = x as usize;
|
||||
|
||||
let coef = (65536_f32 * LOCKSCREEN_DIM) as u32;
|
||||
|
||||
let r = (blurring.totals.buffer[RED_IDX][x] as u32 * BLUR_DIV) >> 16;
|
||||
let g = (blurring.totals.buffer[GREEN_IDX][x] as u32 * BLUR_DIV) >> 16;
|
||||
let b = (blurring.totals.buffer[BLUE_IDX][x] as u32 * BLUR_DIV) >> 16;
|
||||
|
||||
let r = (((coef * r) >> 8) & 0xF800) as u16;
|
||||
let g = (((coef * g) >> 13) & 0x07E0) as u16;
|
||||
let b = (((coef * b) >> 19) & 0x001F) as u16;
|
||||
|
||||
r | g | b
|
||||
} else {
|
||||
let x = x as usize;
|
||||
|
||||
let r = (((blurring.totals.buffer[RED_IDX][x] as u32 * BLUR_DIV) >> 8) & 0xF800) as u16;
|
||||
let g =
|
||||
(((blurring.totals.buffer[GREEN_IDX][x] as u32 * BLUR_DIV) >> 13) & 0x07E0) as u16;
|
||||
let b =
|
||||
(((blurring.totals.buffer[BLUE_IDX][x] as u32 * BLUR_DIV) >> 19) & 0x001F) as u16;
|
||||
r | g | b
|
||||
};
|
||||
|
||||
let j = (2 * x) as usize;
|
||||
img_buffer.buffer[j + 1] = (c >> 8) as u8;
|
||||
img_buffer.buffer[j] = (c & 0xFF) as u8;
|
||||
}
|
||||
|
||||
let done = homescreen_get_fg_text(y, text_info, text_buffer, fg_buffer);
|
||||
homescreen_get_fg_icon(y, text_info, icon_data, fg_buffer);
|
||||
|
||||
dma2d_wait_for_transfer();
|
||||
dma2d_setup_4bpp_over_16bpp(text_info.text_color.into());
|
||||
unsafe {
|
||||
dma2d_start_blend(&fg_buffer.buffer, &img_buffer.buffer, WIDTH);
|
||||
}
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn homescreen_line(
|
||||
icon_data: &[u8],
|
||||
text_buffer: &mut BufferText,
|
||||
text_info: HomescreenTextInfo,
|
||||
data_buffer: &mut BufferJpeg,
|
||||
fg_buffer: &mut BufferLine4bpp,
|
||||
img_buffer: &mut BufferLine16bpp,
|
||||
mcu_height: i16,
|
||||
y: i16,
|
||||
) -> bool {
|
||||
let image_data = get_data(data_buffer, y, mcu_height);
|
||||
fg_buffer.buffer.fill(0);
|
||||
|
||||
for x in 0..HOMESCREEN_IMAGE_WIDTH {
|
||||
let d = image_data[x as usize];
|
||||
|
||||
let c = if homescreen_dim_area(x, y) {
|
||||
let coef = (65536_f32 * HOMESCREEN_DIM) as u32;
|
||||
|
||||
let r = (d & 0xF800) >> 8;
|
||||
let g = (d & 0x07E0) >> 3;
|
||||
let b = (d & 0x001F) << 3;
|
||||
|
||||
let r = (((coef * r as u32) >> 8) & 0xF800) as u16;
|
||||
let g = (((coef * g as u32) >> 13) & 0x07E0) as u16;
|
||||
let b = (((coef * b as u32) >> 19) & 0x001F) as u16;
|
||||
r | g | b
|
||||
} else {
|
||||
d
|
||||
};
|
||||
|
||||
let j = 2 * x as usize;
|
||||
img_buffer.buffer[j + 1] = (c >> 8) as u8;
|
||||
img_buffer.buffer[j] = (c & 0xFF) as u8;
|
||||
}
|
||||
|
||||
let done = homescreen_get_fg_text(y, text_info, text_buffer, fg_buffer);
|
||||
homescreen_get_fg_icon(y, text_info, icon_data, fg_buffer);
|
||||
|
||||
dma2d_wait_for_transfer();
|
||||
dma2d_setup_4bpp_over_16bpp(text_info.text_color.into());
|
||||
unsafe {
|
||||
dma2d_start_blend(&fg_buffer.buffer, &img_buffer.buffer, WIDTH);
|
||||
}
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
fn homescreen_next_text(
|
||||
texts: &[HomescreenText],
|
||||
text_buffer: &mut BufferText,
|
||||
icon_data: &mut [u8],
|
||||
text_info: HomescreenTextInfo,
|
||||
text_idx: usize,
|
||||
) -> (HomescreenTextInfo, usize) {
|
||||
let mut next_text_idx = text_idx;
|
||||
let mut next_text_info = text_info;
|
||||
|
||||
if next_text_idx < texts.len() {
|
||||
if let Some(txt) = texts.get(next_text_idx) {
|
||||
text_buffer.buffer.fill(0);
|
||||
next_text_info = homescreen_position_text(txt, text_buffer, icon_data);
|
||||
next_text_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
(next_text_info, next_text_idx)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn update_accs_add(data: &[u16], idx: usize, acc_r: &mut u16, acc_g: &mut u16, acc_b: &mut u16) {
|
||||
let d = data[idx];
|
||||
let r = (d & 0xF800) >> 8;
|
||||
let g = (d & 0x07E0) >> 3;
|
||||
let b = (d & 0x001F) << 3;
|
||||
*acc_r += r;
|
||||
*acc_g += g;
|
||||
*acc_b += b;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn update_accs_sub(data: &[u16], idx: usize, acc_r: &mut u16, acc_g: &mut u16, acc_b: &mut u16) {
|
||||
let d = data[idx];
|
||||
let r = (d & 0xF800) >> 8;
|
||||
let g = (d & 0x07E0) >> 3;
|
||||
let b = (d & 0x001F) << 3;
|
||||
*acc_r -= r;
|
||||
*acc_g -= g;
|
||||
*acc_b -= b;
|
||||
}
|
||||
|
||||
struct BlurringContext {
|
||||
mem: BufferBlurring,
|
||||
pub totals: BufferBlurringTotals,
|
||||
line_num: i16,
|
||||
add_idx: usize,
|
||||
rem_idx: usize,
|
||||
}
|
||||
|
||||
impl BlurringContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mem: BufferBlurring::get_cleared(),
|
||||
totals: BufferBlurringTotals::get_cleared(),
|
||||
line_num: 0,
|
||||
add_idx: 0,
|
||||
rem_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
let lines = &mut self.mem.buffer[0..DECOMP_LINES];
|
||||
for (i, total) in self.totals.buffer.iter_mut().enumerate() {
|
||||
for line in lines.iter_mut() {
|
||||
line[i].fill(0);
|
||||
}
|
||||
total.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
// computes color averages for one line of image data
|
||||
fn compute_line_avgs(&mut self, buffer: &mut BufferJpeg, mcu_height: i16) {
|
||||
let lines = &mut self.mem.buffer[0..DECOMP_LINES];
|
||||
let mut acc_r = 0;
|
||||
let mut acc_g = 0;
|
||||
let mut acc_b = 0;
|
||||
let data = get_data(buffer, self.line_num, mcu_height);
|
||||
|
||||
for i in -BLUR_RADIUS..=BLUR_RADIUS {
|
||||
let ic = i.clamp(0, HOMESCREEN_IMAGE_WIDTH - 1) as usize;
|
||||
update_accs_add(data, ic, &mut acc_r, &mut acc_g, &mut acc_b);
|
||||
}
|
||||
|
||||
for i in 0..HOMESCREEN_IMAGE_WIDTH {
|
||||
lines[self.add_idx][RED_IDX][i as usize] = acc_r;
|
||||
lines[self.add_idx][GREEN_IDX][i as usize] = acc_g;
|
||||
lines[self.add_idx][BLUE_IDX][i as usize] = acc_b;
|
||||
|
||||
// clamping handles left and right edges
|
||||
let ic = (i - BLUR_RADIUS).clamp(0, HOMESCREEN_IMAGE_WIDTH - 1) as usize;
|
||||
let ic2 =
|
||||
(i + BLUR_SIZE as i16 - BLUR_RADIUS).clamp(0, HOMESCREEN_IMAGE_WIDTH - 1) as usize;
|
||||
update_accs_add(data, ic2, &mut acc_r, &mut acc_g, &mut acc_b);
|
||||
update_accs_sub(data, ic, &mut acc_r, &mut acc_g, &mut acc_b);
|
||||
}
|
||||
self.line_num += 1;
|
||||
}
|
||||
|
||||
// adds one line of averages to sliding total averages
|
||||
fn vertical_avg_add(&mut self) {
|
||||
let lines = &mut self.mem.buffer[0..DECOMP_LINES];
|
||||
for i in 0..HOMESCREEN_IMAGE_WIDTH as usize {
|
||||
self.totals.buffer[RED_IDX][i] += lines[self.add_idx][RED_IDX][i];
|
||||
self.totals.buffer[GREEN_IDX][i] += lines[self.add_idx][GREEN_IDX][i];
|
||||
self.totals.buffer[BLUE_IDX][i] += lines[self.add_idx][BLUE_IDX][i];
|
||||
}
|
||||
}
|
||||
|
||||
// adds one line and removes one line of averages to/from sliding total averages
|
||||
fn vertical_avg(&mut self) {
|
||||
let lines = &mut self.mem.buffer[0..DECOMP_LINES];
|
||||
for i in 0..HOMESCREEN_IMAGE_WIDTH as usize {
|
||||
self.totals.buffer[RED_IDX][i] += lines[self.add_idx][RED_IDX][i];
|
||||
self.totals.buffer[GREEN_IDX][i] += lines[self.add_idx][GREEN_IDX][i];
|
||||
self.totals.buffer[BLUE_IDX][i] += lines[self.add_idx][BLUE_IDX][i];
|
||||
self.totals.buffer[RED_IDX][i] -= lines[self.rem_idx][RED_IDX][i];
|
||||
self.totals.buffer[GREEN_IDX][i] -= lines[self.rem_idx][GREEN_IDX][i];
|
||||
self.totals.buffer[BLUE_IDX][i] -= lines[self.rem_idx][BLUE_IDX][i];
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_add(&mut self) {
|
||||
self.add_idx += 1;
|
||||
if self.add_idx >= DECOMP_LINES {
|
||||
self.add_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_rem(&mut self) {
|
||||
self.rem_idx += 1;
|
||||
if self.rem_idx >= DECOMP_LINES {
|
||||
self.rem_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_line_num(&self) -> i16 {
|
||||
self.line_num
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_data(buffer: &mut BufferJpeg, line_num: i16, mcu_height: i16) -> &mut [u16] {
|
||||
let data_start = ((line_num % mcu_height) * WIDTH) as usize;
|
||||
let data_end = (((line_num % mcu_height) + 1) * WIDTH) as usize;
|
||||
&mut buffer.buffer[data_start..data_end]
|
||||
}
|
||||
|
||||
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 mut text_buffer = BufferText::get_cleared();
|
||||
let mut fg_buffer_0 = BufferLine4bpp::get_cleared();
|
||||
let mut img_buffer_0 = BufferLine16bpp::get_cleared();
|
||||
let mut fg_buffer_1 = BufferLine4bpp::get_cleared();
|
||||
let mut img_buffer_1 = BufferLine16bpp::get_cleared();
|
||||
|
||||
let mut next_text_idx = 1;
|
||||
let mut text_info =
|
||||
homescreen_position_text(unwrap!(texts.first()), &mut text_buffer, &mut icon_data);
|
||||
|
||||
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(data.get_data(), mcu_height);
|
||||
|
||||
for _ in 0..=BLUR_RADIUS {
|
||||
blurring.vertical_avg_add();
|
||||
}
|
||||
blurring.inc_add();
|
||||
|
||||
// load enough values to be able to compute first line averages
|
||||
for _ in 0..BLUR_RADIUS {
|
||||
blurring.compute_line_avgs(data.get_data(), mcu_height);
|
||||
blurring.vertical_avg_add();
|
||||
blurring.inc_add();
|
||||
|
||||
if (blurring.get_line_num() % mcu_height) == 0 {
|
||||
data.decompress();
|
||||
}
|
||||
}
|
||||
|
||||
for y in 0..HEIGHT {
|
||||
// several lines have been already decompressed before this loop, adjust for
|
||||
// that
|
||||
if y < HOMESCREEN_IMAGE_HEIGHT - (BLUR_RADIUS + 1) {
|
||||
blurring.compute_line_avgs(data.get_data(), mcu_height);
|
||||
}
|
||||
|
||||
let done = if y % 2 == 0 {
|
||||
homescreen_line_blurred(
|
||||
&icon_data,
|
||||
&mut text_buffer,
|
||||
&mut fg_buffer_0,
|
||||
&mut img_buffer_0,
|
||||
text_info,
|
||||
&blurring,
|
||||
y,
|
||||
)
|
||||
} else {
|
||||
homescreen_line_blurred(
|
||||
&icon_data,
|
||||
&mut text_buffer,
|
||||
&mut fg_buffer_1,
|
||||
&mut img_buffer_1,
|
||||
text_info,
|
||||
&blurring,
|
||||
y,
|
||||
)
|
||||
};
|
||||
|
||||
if done {
|
||||
(text_info, next_text_idx) = homescreen_next_text(
|
||||
texts,
|
||||
&mut text_buffer,
|
||||
&mut icon_data,
|
||||
text_info,
|
||||
next_text_idx,
|
||||
);
|
||||
}
|
||||
|
||||
blurring.vertical_avg();
|
||||
|
||||
// handling bottom edge case: stop incrementing counter, adding the edge value
|
||||
// for the rest of image
|
||||
// the extra -1 is to indicate that this was the last decompressed line,
|
||||
// in the next pass the docompression and compute_line_avgs won't happen
|
||||
if y < HOMESCREEN_IMAGE_HEIGHT - (BLUR_RADIUS + 1) - 1 {
|
||||
blurring.inc_add();
|
||||
}
|
||||
|
||||
if y == HOMESCREEN_IMAGE_HEIGHT {
|
||||
// reached end of image, clear avgs (display black)
|
||||
blurring.clear();
|
||||
}
|
||||
|
||||
// only start incrementing remove index when enough lines have been loaded
|
||||
if y >= (BLUR_RADIUS) {
|
||||
blurring.inc_rem();
|
||||
}
|
||||
|
||||
if (blurring.get_line_num() % mcu_height) == 0 && (blurring.get_line_num() < HEIGHT) {
|
||||
data.decompress();
|
||||
}
|
||||
}
|
||||
dma2d_wait_for_transfer();
|
||||
}
|
||||
|
||||
pub fn homescreen(
|
||||
data: &mut dyn HomescreenDecompressor,
|
||||
texts: &[HomescreenText],
|
||||
notification: Option<HomescreenNotification>,
|
||||
notification_only: bool,
|
||||
) {
|
||||
let mut icon_data = [0_u8; (HOMESCREEN_MAX_ICON_SIZE * HOMESCREEN_MAX_ICON_SIZE / 2) as usize];
|
||||
|
||||
let mut text_buffer = BufferText::get_cleared();
|
||||
let mut fg_buffer_0 = BufferLine4bpp::get_cleared();
|
||||
let mut img_buffer_0 = BufferLine16bpp::get_cleared();
|
||||
let mut fg_buffer_1 = BufferLine4bpp::get_cleared();
|
||||
let mut img_buffer_1 = BufferLine16bpp::get_cleared();
|
||||
|
||||
let mut next_text_idx = 0;
|
||||
let mut text_info = if let Some(notification) = notification {
|
||||
rect_fill_rounded_buffer(
|
||||
Rect::from_top_left_and_size(
|
||||
Point::new(NOTIFICATION_BORDER, 0),
|
||||
Offset::new(WIDTH - NOTIFICATION_BORDER * 2, NOTIFICATION_HEIGHT),
|
||||
),
|
||||
2,
|
||||
&mut text_buffer,
|
||||
);
|
||||
let area = Rect::new(
|
||||
Point::new(0, NOTIFICATION_BORDER),
|
||||
Point::new(WIDTH, NOTIFICATION_HEIGHT + NOTIFICATION_BORDER),
|
||||
);
|
||||
HomescreenTextInfo {
|
||||
text_area: area,
|
||||
text_width: WIDTH,
|
||||
text_color: notification.color,
|
||||
icon_area: None,
|
||||
}
|
||||
} else {
|
||||
next_text_idx += 1;
|
||||
homescreen_position_text(unwrap!(texts.first()), &mut text_buffer, &mut icon_data)
|
||||
};
|
||||
|
||||
set_window(screen());
|
||||
|
||||
let mcu_height = data.get_height();
|
||||
|
||||
for y in 0..HEIGHT {
|
||||
if (y % mcu_height) == 0 {
|
||||
data.decompress();
|
||||
}
|
||||
|
||||
let done = if y % 2 == 0 {
|
||||
homescreen_line(
|
||||
&icon_data,
|
||||
&mut text_buffer,
|
||||
text_info,
|
||||
data.get_data(),
|
||||
&mut fg_buffer_0,
|
||||
&mut img_buffer_0,
|
||||
mcu_height,
|
||||
y,
|
||||
)
|
||||
} else {
|
||||
homescreen_line(
|
||||
&icon_data,
|
||||
&mut text_buffer,
|
||||
text_info,
|
||||
data.get_data(),
|
||||
&mut fg_buffer_1,
|
||||
&mut img_buffer_1,
|
||||
mcu_height,
|
||||
y,
|
||||
)
|
||||
};
|
||||
|
||||
if done {
|
||||
if notification.is_some() && next_text_idx == 0 {
|
||||
//finished notification area, let interrupt and draw the text
|
||||
let notification = unwrap!(notification);
|
||||
|
||||
let style = TextStyle {
|
||||
background_color: notification.color,
|
||||
..theme::TEXT_BOLD
|
||||
};
|
||||
|
||||
dma2d_wait_for_transfer();
|
||||
|
||||
drop(fg_buffer_0);
|
||||
drop(fg_buffer_1);
|
||||
|
||||
icon_text_center(
|
||||
text_info.text_area.center(),
|
||||
notification.icon,
|
||||
8,
|
||||
notification.text,
|
||||
style,
|
||||
Offset::new(1, -2),
|
||||
);
|
||||
|
||||
fg_buffer_0 = BufferLine4bpp::get_cleared();
|
||||
fg_buffer_1 = BufferLine4bpp::get_cleared();
|
||||
|
||||
set_window(
|
||||
screen()
|
||||
.split_top(NOTIFICATION_HEIGHT + NOTIFICATION_BORDER)
|
||||
.1,
|
||||
);
|
||||
}
|
||||
|
||||
if notification_only && next_text_idx == 0 {
|
||||
dma2d_wait_for_transfer();
|
||||
return;
|
||||
}
|
||||
|
||||
(text_info, next_text_idx) = homescreen_next_text(
|
||||
texts,
|
||||
&mut text_buffer,
|
||||
&mut icon_data,
|
||||
text_info,
|
||||
next_text_idx,
|
||||
);
|
||||
}
|
||||
}
|
||||
dma2d_wait_for_transfer();
|
||||
}
|
Loading…
Reference in New Issue
Block a user