mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-22 22:38:08 +00:00
feat(core): add basic support for efficient frame buffer graphics
[no changelog]
This commit is contained in:
parent
2c894a66ef
commit
df42c99bc9
@ -123,7 +123,7 @@ void ui_screen_boot(const vendor_header *const vhdr,
|
||||
|
||||
#endif
|
||||
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ void ui_screen_boot_wait(int wait_seconds) {
|
||||
boot_background);
|
||||
display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 5, wait_str, -1,
|
||||
FONT_NORMAL, COLOR_BL_BG, boot_background);
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ void ui_screen_boot_click(void) {
|
||||
display_bar(0, BOOT_WAIT_Y_TOP, DISPLAY_RESX, BOOT_WAIT_HEIGHT,
|
||||
boot_background);
|
||||
bld_continue_label(boot_background);
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
display_refresh();
|
||||
ui_click();
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ uint32_t rgb565_to_rgb888(uint16_t color) {
|
||||
res |= ((((((uint32_t)color & 0xF800) >> 11) * 527) + 23) >> 6) << 16;
|
||||
res |= ((((((uint32_t)color & 0x07E0) >> 5) * 259) + 33) >> 6) << 8;
|
||||
res |= ((((((uint32_t)color & 0x001F) >> 0) * 527) + 23) >> 6) << 0;
|
||||
res |= 0xFF000000;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -37,5 +38,5 @@ uint32_t interpolate_rgb888_color(uint32_t color0, uint32_t color1,
|
||||
((color1 & 0xFF00) >> 8) * (15 - step)) /
|
||||
15;
|
||||
cb = ((color0 & 0x00FF) * step + (color1 & 0x00FF) * (15 - step)) / 15;
|
||||
return (cr << 16) | (cg << 8) | cb;
|
||||
return (cr << 16) | (cg << 8) | cb | 0xFF000000;
|
||||
}
|
||||
|
@ -51,6 +51,9 @@ static inline void clamp_coords(int x, int y, int w, int h, int *x0, int *y0,
|
||||
}
|
||||
|
||||
void display_clear(void) {
|
||||
#ifdef DISPLAY_EFFICIENT_CLEAR
|
||||
display_efficient_clear();
|
||||
#else
|
||||
const int saved_orientation = display_get_orientation();
|
||||
|
||||
display_reset_state();
|
||||
@ -68,7 +71,8 @@ void display_clear(void) {
|
||||
// if valid, go back to the saved orientation
|
||||
display_orientation(saved_orientation);
|
||||
// flag display for refresh
|
||||
PIXELDATA_DIRTY();
|
||||
#endif
|
||||
display_pixeldata_dirty();
|
||||
}
|
||||
|
||||
void display_bar(int x, int y, int w, int h, uint16_t c) {
|
||||
@ -80,104 +84,7 @@ void display_bar(int x, int y, int w, int h, uint16_t c) {
|
||||
for (int i = 0; i < (x1 - x0 + 1) * (y1 - y0 + 1); i++) {
|
||||
PIXELDATA(c);
|
||||
}
|
||||
PIXELDATA_DIRTY();
|
||||
}
|
||||
|
||||
#define CORNER_RADIUS 16
|
||||
|
||||
static const uint8_t cornertable[CORNER_RADIUS * CORNER_RADIUS] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 9, 12, 14, 15, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 3, 9, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0,
|
||||
0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 3, 12, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 0, 0, 0, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0,
|
||||
8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 3, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 9, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
};
|
||||
|
||||
void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
|
||||
uint8_t r) {
|
||||
if (r != 2 && r != 4 && r != 8 && r != 16) {
|
||||
return;
|
||||
} else {
|
||||
r = 16 / r;
|
||||
}
|
||||
uint16_t colortable[16] = {0};
|
||||
set_color_table(colortable, c, b);
|
||||
x += DISPLAY_OFFSET.x;
|
||||
y += DISPLAY_OFFSET.y;
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
clamp_coords(x, y, w, h, &x0, &y0, &x1, &y1);
|
||||
display_set_window(x0, y0, x1, y1);
|
||||
for (int j = y0; j <= y1; j++) {
|
||||
for (int i = x0; i <= x1; i++) {
|
||||
int rx = i - x;
|
||||
int ry = j - y;
|
||||
if (rx < CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
|
||||
uint8_t c = cornertable[rx * r + ry * r * CORNER_RADIUS];
|
||||
PIXELDATA(colortable[c]);
|
||||
} else if (rx < CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
|
||||
uint8_t c = cornertable[rx * r + (h - 1 - ry) * r * CORNER_RADIUS];
|
||||
PIXELDATA(colortable[c]);
|
||||
} else if (rx >= w - CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
|
||||
uint8_t c = cornertable[(w - 1 - rx) * r + ry * r * CORNER_RADIUS];
|
||||
PIXELDATA(colortable[c]);
|
||||
} else if (rx >= w - CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
|
||||
uint8_t c =
|
||||
cornertable[(w - 1 - rx) * r + (h - 1 - ry) * r * CORNER_RADIUS];
|
||||
PIXELDATA(colortable[c]);
|
||||
} else {
|
||||
PIXELDATA(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
PIXELDATA_DIRTY();
|
||||
}
|
||||
|
||||
void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r,
|
||||
buffer_text_t *buffer) {
|
||||
if (h > TEXT_BUFFER_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
if (r != 2 && r != 4 && r != 8 && r != 16) {
|
||||
return;
|
||||
} else {
|
||||
r = 16 / r;
|
||||
}
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
clamp_coords(x, y, w, h, &x0, &y0, &x1, &y1);
|
||||
for (int j = y0; j <= y1; j++) {
|
||||
for (int i = x0; i <= x1; i++) {
|
||||
int rx = i - x;
|
||||
int ry = j - y;
|
||||
int p = j * DISPLAY_RESX + i;
|
||||
uint8_t c = 0;
|
||||
if (rx < CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
|
||||
c = cornertable[rx * r + ry * r * CORNER_RADIUS];
|
||||
} else if (rx < CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
|
||||
c = cornertable[rx * r + (h - 1 - ry) * r * CORNER_RADIUS];
|
||||
} else if (rx >= w - CORNER_RADIUS / r && ry < CORNER_RADIUS / r) {
|
||||
c = cornertable[(w - 1 - rx) * r + ry * r * CORNER_RADIUS];
|
||||
} else if (rx >= w - CORNER_RADIUS / r && ry >= h - CORNER_RADIUS / r) {
|
||||
c = cornertable[(w - 1 - rx) * r + (h - 1 - ry) * r * CORNER_RADIUS];
|
||||
} else {
|
||||
c = 15;
|
||||
}
|
||||
int b = p / 2;
|
||||
if (p % 2) {
|
||||
buffer->buffer[b] |= c << 4;
|
||||
} else {
|
||||
buffer->buffer[b] |= (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
display_pixeldata_dirty();
|
||||
}
|
||||
|
||||
void display_text_render_buffer(const char *text, int textlen, int font,
|
||||
@ -357,7 +264,7 @@ void display_print(const char *text, int textlen) {
|
||||
PIXELDATA(display_print_bgcolor);
|
||||
}
|
||||
}
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
display_refresh();
|
||||
}
|
||||
|
||||
@ -384,6 +291,67 @@ void display_printf(const char *fmt, ...) {
|
||||
|
||||
#endif // TREZOR_PRINT_DISABLE
|
||||
|
||||
#ifdef FRAMEBUFFER
|
||||
static void display_text_render(int x, int y, const char *text, int textlen,
|
||||
int font, uint16_t fgcolor, uint16_t bgcolor) {
|
||||
// determine text length if not provided
|
||||
if (textlen < 0) {
|
||||
textlen = strlen(text);
|
||||
}
|
||||
|
||||
int total_adv = 0;
|
||||
|
||||
uint32_t *fb = display_get_fb_addr();
|
||||
|
||||
uint16_t colortable[16] = {0};
|
||||
set_color_table(colortable, fgcolor, bgcolor);
|
||||
|
||||
// render glyphs
|
||||
for (int c_idx = 0; c_idx < textlen; c_idx++) {
|
||||
const uint8_t *g = font_get_glyph(font, (uint8_t)text[c_idx]);
|
||||
if (!g) continue;
|
||||
const uint8_t w = g[0]; // width
|
||||
const uint8_t h = g[1]; // height
|
||||
const uint8_t adv = g[2]; // advance
|
||||
const uint8_t bearX = g[3]; // bearingX
|
||||
const uint8_t bearY = g[4]; // bearingY
|
||||
if (w && h) {
|
||||
for (int j = 0; j < h; j++) {
|
||||
for (int i = 0; i < w; i++) {
|
||||
const int a = i + j * w;
|
||||
#if TREZOR_FONT_BPP == 1
|
||||
const uint8_t c = ((g[5 + a / 8] >> (7 - (a % 8) * 1)) & 0x01) * 15;
|
||||
#elif TREZOR_FONT_BPP == 2
|
||||
const uint8_t c = ((g[5 + a / 4] >> (6 - (a % 4) * 2)) & 0x03) * 5;
|
||||
#elif TREZOR_FONT_BPP == 4
|
||||
const uint8_t c = (g[5 + a / 2] >> (4 - (a % 2) * 4)) & 0x0F;
|
||||
#elif TREZOR_FONT_BPP == 8
|
||||
#error Rendering into buffer not supported when using TREZOR_FONT_BPP = 8
|
||||
// const uint8_t c = g[5 + a / 1] >> 4;
|
||||
#else
|
||||
#error Unsupported TREZOR_FONT_BPP value
|
||||
#endif
|
||||
|
||||
int x_pos = x + i + total_adv + bearX;
|
||||
int y_pos = y + j - bearY;
|
||||
|
||||
if (y_pos < 0) continue;
|
||||
|
||||
if (x_pos >= DISPLAY_FRAMEBUFFER_WIDTH || x_pos < 0 ||
|
||||
y_pos >= DISPLAY_FRAMEBUFFER_HEIGHT || y_pos < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
display_pixel((uint8_t *)fb, x_pos, y_pos, colortable[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
total_adv += adv;
|
||||
}
|
||||
display_pixeldata_dirty();
|
||||
}
|
||||
|
||||
#else
|
||||
static void display_text_render(int x, int y, const char *text, int textlen,
|
||||
int font, uint16_t fgcolor, uint16_t bgcolor) {
|
||||
// determine text length if not provided
|
||||
@ -431,8 +399,9 @@ static void display_text_render(int x, int y, const char *text, int textlen,
|
||||
}
|
||||
x += adv;
|
||||
}
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
}
|
||||
#endif
|
||||
|
||||
void display_text(int x, int y, const char *text, int textlen, int font,
|
||||
uint16_t fgcolor, uint16_t bgcolor) {
|
||||
@ -552,7 +521,7 @@ void display_qrcode(int x, int y, const char *data, uint8_t scale) {
|
||||
}
|
||||
}
|
||||
}
|
||||
PIXELDATA_DIRTY();
|
||||
display_pixeldata_dirty();
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -610,5 +579,3 @@ void display_utf8_substr(const char *buf_start, size_t buf_len, int char_off,
|
||||
*out_start = buf_start + i_start;
|
||||
*out_len = i - i_start;
|
||||
}
|
||||
|
||||
void display_pixeldata_dirty(void) { PIXELDATA_DIRTY(); }
|
||||
|
@ -37,24 +37,11 @@ typedef enum {
|
||||
TOIF_GRAYSCALE_EH = 3, // even hi
|
||||
} toif_format_t;
|
||||
|
||||
// provided by port
|
||||
|
||||
void display_init(void);
|
||||
void display_init_seq(void);
|
||||
void display_refresh(void);
|
||||
const char *display_save(const char *prefix);
|
||||
void display_clear_save(void);
|
||||
|
||||
// provided by common
|
||||
|
||||
void display_clear(void);
|
||||
|
||||
void display_bar(int x, int y, int w, int h, uint16_t c);
|
||||
void display_bar_radius(int x, int y, int w, int h, uint16_t c, uint16_t b,
|
||||
uint8_t r);
|
||||
void display_bar_radius_buffer(int x, int y, int w, int h, uint8_t r,
|
||||
buffer_text_t *buffer);
|
||||
|
||||
bool display_toif_info(const uint8_t *buf, uint32_t len, uint16_t *out_w,
|
||||
uint16_t *out_h, toif_format_t *out_format);
|
||||
|
||||
@ -86,7 +73,4 @@ void display_fade(int start, int end, int delay);
|
||||
void display_utf8_substr(const char *buf_start, size_t buf_len, int char_off,
|
||||
int char_len, const char **out_start, int *out_len);
|
||||
|
||||
// pixeldata accessor
|
||||
void display_pixeldata_dirty();
|
||||
|
||||
#endif
|
||||
|
@ -24,15 +24,28 @@
|
||||
#include "common.h"
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#ifndef DISPLAY_FRAMEBUFFER_OFFSET_Y
|
||||
#define DISPLAY_FRAMEBUFFER_OFFSET_Y 0
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_FRAMEBUFFER_OFFSET_X
|
||||
#define DISPLAY_FRAMEBUFFER_OFFSET_X 0
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_FRAMEBUFFER_WIDTH
|
||||
#define DISPLAY_FRAMEBUFFER_WIDTH 0
|
||||
#endif
|
||||
|
||||
#ifndef DISPLAY_FRAMEBUFFER_HEIGHT
|
||||
#define DISPLAY_FRAMEBUFFER_HEIGHT 0
|
||||
#endif
|
||||
|
||||
#ifndef PIXELDATA
|
||||
#define PIXELDATA(c) display_pixeldata(c)
|
||||
#endif
|
||||
|
||||
void display_pixeldata(uint16_t c);
|
||||
|
||||
#ifndef PIXELDATA_DIRTY
|
||||
#define PIXELDATA_DIRTY()
|
||||
#endif
|
||||
void display_pixeldata_dirty(void);
|
||||
|
||||
void display_reset_state();
|
||||
|
||||
@ -48,4 +61,10 @@ void display_refresh(void);
|
||||
const char *display_save(const char *prefix);
|
||||
void display_clear_save(void);
|
||||
|
||||
void display_efficient_clear(void);
|
||||
uint32_t *display_get_fb_addr(void);
|
||||
uint8_t *display_get_wr_addr(void);
|
||||
void display_shift_window(uint16_t pixels);
|
||||
uint16_t display_get_window_offset(void);
|
||||
|
||||
#endif //_DISPLAY_INTERFACE_H
|
||||
|
@ -14,6 +14,8 @@ micropython = []
|
||||
protobuf = ["micropython"]
|
||||
ui = []
|
||||
dma2d = []
|
||||
framebuffer = []
|
||||
framebuffer32bit = []
|
||||
ui_debug = []
|
||||
ui_bounds = []
|
||||
bootloader = []
|
||||
|
@ -296,14 +296,17 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("display_text")
|
||||
.allowlist_function("display_text_render_buffer")
|
||||
.allowlist_function("display_text_width")
|
||||
.allowlist_function("display_bar")
|
||||
.allowlist_function("display_bar_radius")
|
||||
.allowlist_function("display_bar_radius_buffer")
|
||||
.allowlist_function("display_pixeldata")
|
||||
.allowlist_function("display_pixeldata_dirty")
|
||||
.allowlist_function("display_set_window")
|
||||
.allowlist_function("display_sync")
|
||||
.allowlist_function("display_get_fb_addr")
|
||||
.allowlist_function("display_get_wr_addr")
|
||||
.allowlist_var("DISPLAY_DATA_ADDRESS")
|
||||
.allowlist_var("DISPLAY_FRAMEBUFFER_WIDTH")
|
||||
.allowlist_var("DISPLAY_FRAMEBUFFER_HEIGHT")
|
||||
.allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_X")
|
||||
.allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_Y")
|
||||
.allowlist_var("DISPLAY_RESX")
|
||||
.allowlist_var("DISPLAY_RESY")
|
||||
.allowlist_type("toif_format_t")
|
||||
@ -332,12 +335,15 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("hal_delay")
|
||||
.allowlist_function("hal_ticks_ms")
|
||||
// dma2d
|
||||
.allowlist_function("dma2d_setup_const")
|
||||
.allowlist_function("dma2d_setup_4bpp")
|
||||
.allowlist_function("dma2d_setup_16bpp")
|
||||
.allowlist_function("dma2d_setup_4bpp_over_4bpp")
|
||||
.allowlist_function("dma2d_setup_4bpp_over_16bpp")
|
||||
.allowlist_function("dma2d_start")
|
||||
.allowlist_function("dma2d_start_blend")
|
||||
.allowlist_function("dma2d_start_const")
|
||||
.allowlist_function("dma2d_start_const_multiline")
|
||||
.allowlist_function("dma2d_wait_for_transfer")
|
||||
//buffers
|
||||
.allowlist_function("buffers_get_line_16bpp")
|
||||
@ -352,6 +358,7 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("buffers_free_jpeg_work")
|
||||
.allowlist_function("buffers_get_blurring")
|
||||
.allowlist_function("buffers_free_blurring")
|
||||
.allowlist_var("TEXT_BUFFER_HEIGHT")
|
||||
.no_copy("buffer_line_16bpp_t")
|
||||
.no_copy("buffer_line_4bpp_t")
|
||||
.no_copy("buffer_text_t")
|
||||
|
@ -5,6 +5,8 @@ use core::{
|
||||
|
||||
use super::ffi;
|
||||
|
||||
pub use ffi::TEXT_BUFFER_HEIGHT;
|
||||
|
||||
macro_rules! buffer_wrapper {
|
||||
($rust_name: ident, $type: ident, $get: ident, $free: ident) => {
|
||||
pub struct $rust_name(ptr::NonNull<ffi::$type>);
|
||||
|
@ -6,6 +6,20 @@ use crate::trezorhal::buffers::BufferText;
|
||||
|
||||
pub use ffi::{DISPLAY_RESX, DISPLAY_RESY};
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
pub use ffi::{
|
||||
DISPLAY_FRAMEBUFFER_HEIGHT, DISPLAY_FRAMEBUFFER_OFFSET_X, DISPLAY_FRAMEBUFFER_OFFSET_Y,
|
||||
DISPLAY_FRAMEBUFFER_WIDTH,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "framebuffer", not(feature = "framebuffer32bit")))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FrameBuffer(*mut u16);
|
||||
|
||||
#[cfg(all(feature = "framebuffer", feature = "framebuffer32bit"))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct FrameBuffer(*mut u32);
|
||||
|
||||
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
|
||||
pub enum ToifFormat {
|
||||
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _,
|
||||
@ -74,37 +88,6 @@ pub fn text_baseline(font: i32) -> i16 {
|
||||
unsafe { ffi::font_baseline(font).try_into().unwrap_or(i16::MAX) }
|
||||
}
|
||||
|
||||
pub fn bar(x: i16, y: i16, w: i16, h: i16, fgcolor: u16) {
|
||||
unsafe { ffi::display_bar(x.into(), y.into(), w.into(), h.into(), fgcolor) }
|
||||
}
|
||||
|
||||
pub fn bar_radius(x: i16, y: i16, w: i16, h: i16, fgcolor: u16, bgcolor: u16, radius: u8) {
|
||||
unsafe {
|
||||
ffi::display_bar_radius(
|
||||
x.into(),
|
||||
y.into(),
|
||||
w.into(),
|
||||
h.into(),
|
||||
fgcolor,
|
||||
bgcolor,
|
||||
radius,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bar_radius_buffer(x: i16, y: i16, w: i16, h: i16, radius: u8, buffer: &mut BufferText) {
|
||||
unsafe {
|
||||
ffi::display_bar_radius_buffer(
|
||||
x.into(),
|
||||
y.into(),
|
||||
w.into(),
|
||||
h.into(),
|
||||
radius,
|
||||
buffer.deref_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(all(feature = "disp_i8080_16bit_dw", not(feature = "disp_i8080_8bit_dw")))]
|
||||
pub fn pixeldata(c: u16) {
|
||||
@ -113,6 +96,11 @@ pub fn pixeldata(c: u16) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
pub fn get_fb_addr() -> FrameBuffer {
|
||||
unsafe { FrameBuffer(ffi::display_get_fb_addr() as _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(feature = "disp_i8080_8bit_dw")]
|
||||
pub fn pixeldata(c: u16) {
|
||||
@ -122,6 +110,30 @@ pub fn pixeldata(c: u16) {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(all(feature = "framebuffer", not(feature = "framebuffer32bit")))]
|
||||
pub fn pixel(fb: FrameBuffer, x: i16, y: i16, c: u16) {
|
||||
unsafe {
|
||||
let addr = fb.0.offset(
|
||||
((y as u32 + DISPLAY_FRAMEBUFFER_OFFSET_Y) * DISPLAY_FRAMEBUFFER_WIDTH
|
||||
+ (x as u32 + DISPLAY_FRAMEBUFFER_OFFSET_X)) as isize,
|
||||
);
|
||||
addr.write_volatile(c);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(all(feature = "framebuffer", feature = "framebuffer32bit"))]
|
||||
pub fn pixel(fb: FrameBuffer, x: i16, y: i16, c: u32) {
|
||||
unsafe {
|
||||
let addr = fb.0.offset(
|
||||
((y as u32 + DISPLAY_FRAMEBUFFER_OFFSET_Y) * DISPLAY_FRAMEBUFFER_WIDTH
|
||||
+ (x as u32 + DISPLAY_FRAMEBUFFER_OFFSET_X)) as isize,
|
||||
);
|
||||
addr.write_volatile(c);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(any(feature = "disp_i8080_16bit_dw", feature = "disp_i8080_8bit_dw")))]
|
||||
pub fn pixeldata(c: u16) {
|
||||
|
@ -1,6 +1,10 @@
|
||||
use super::ffi;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
pub fn dma2d_setup_const() {
|
||||
unsafe { ffi::dma2d_setup_const() }
|
||||
}
|
||||
pub fn dma2d_setup_4bpp(fg_color: u16, bg_color: u16) {
|
||||
unsafe { ffi::dma2d_setup_4bpp(fg_color, bg_color) }
|
||||
}
|
||||
@ -30,7 +34,7 @@ pub unsafe fn dma2d_start(buffer: &[u8], pixels: i16) {
|
||||
unsafe {
|
||||
ffi::dma2d_start(
|
||||
buffer.as_ptr() as _,
|
||||
ffi::DISPLAY_DATA_ADDRESS as _,
|
||||
ffi::display_get_wr_addr() as _,
|
||||
pixels as _,
|
||||
);
|
||||
}
|
||||
@ -50,12 +54,48 @@ pub unsafe fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels:
|
||||
ffi::dma2d_start_blend(
|
||||
overlay_buffer.as_ptr() as _,
|
||||
bg_buffer.as_ptr() as _,
|
||||
ffi::DISPLAY_DATA_ADDRESS as _,
|
||||
ffi::display_get_wr_addr() as _,
|
||||
pixels as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts blending
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because the caller has to guarantee that he:
|
||||
/// 1) doesn't mutate the buffers until the transfer is finished, which is
|
||||
/// guaranteed by calling `dma2d_wait_for_transfer`
|
||||
/// 2) the buffer doesn't get dropped until the transfer is finished
|
||||
/// 3) doesn't call this function while another transfer is running
|
||||
pub unsafe fn dma2d_start_const(color: u16, pixels: i16) {
|
||||
unsafe {
|
||||
ffi::dma2d_start_const(color, ffi::display_get_wr_addr() as _, pixels as _);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
/// Starts blending
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because the caller has to guarantee that he:
|
||||
/// 1) doesn't mutate the buffers until the transfer is finished, which is
|
||||
/// guaranteed by calling `dma2d_wait_for_transfer`
|
||||
/// 2) the buffer doesn't get dropped until the transfer is finished
|
||||
/// 3) doesn't call this function while another transfer is running
|
||||
pub unsafe fn dma2d_start_const_multiline(color: u16, width: i16, height: i16) {
|
||||
unsafe {
|
||||
ffi::dma2d_start_const_multiline(
|
||||
color,
|
||||
ffi::display_get_wr_addr() as _,
|
||||
width as _,
|
||||
height as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dma2d_wait_for_transfer() {
|
||||
unsafe {
|
||||
ffi::dma2d_wait_for_transfer();
|
||||
|
@ -7,6 +7,13 @@ impl Color {
|
||||
pub const fn from_u16(val: u16) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
pub const fn from_u32(val: u32) -> Self {
|
||||
Self::rgb(
|
||||
((val >> 16) & 0xFF) as u8,
|
||||
((val >> 8) & 0xFF) as u8,
|
||||
(val & 0xFF) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r = (r as u16 & 0xF8) << 8;
|
||||
@ -56,6 +63,10 @@ impl Color {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn to_u32(self) -> u32 {
|
||||
((self.r() as u32) << 16) | ((self.g() as u32) << 8) | (self.b() as u32) | 0xff000000
|
||||
}
|
||||
|
||||
pub fn hi_byte(self) -> u8 {
|
||||
(self.to_u16() >> 8) as u8
|
||||
}
|
||||
@ -97,3 +108,15 @@ impl From<Color> for u16 {
|
||||
val.to_u16()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Color {
|
||||
fn from(val: u32) -> Self {
|
||||
Self::from_u32(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for u32 {
|
||||
fn from(val: Color) -> Self {
|
||||
val.to_u32()
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ use crate::{
|
||||
};
|
||||
|
||||
// Reexports
|
||||
use crate::trezorhal::buffers::BufferText;
|
||||
pub use crate::ui::display::toif::Icon;
|
||||
pub use color::Color;
|
||||
pub use font::{Font, Glyph, GlyphMetrics};
|
||||
@ -39,6 +40,13 @@ pub use loader::{
|
||||
loader, loader_indeterminate, loader_small_indeterminate, LOADER_MAX, LOADER_MIN,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "dma2d", feature = "framebuffer"))]
|
||||
use crate::trezorhal::{
|
||||
display::{get_fb_addr, pixel},
|
||||
dma2d::{dma2d_setup_const, dma2d_start_const_multiline},
|
||||
};
|
||||
use crate::ui::constant::WIDTH;
|
||||
|
||||
pub fn backlight() -> u16 {
|
||||
display::backlight(-1) as u16
|
||||
}
|
||||
@ -78,34 +86,223 @@ pub fn fade_backlight(_: u16) {}
|
||||
#[cfg(not(feature = "backlight"))]
|
||||
pub fn fade_backlight_duration(_: u16, _: u32) {}
|
||||
|
||||
#[cfg(not(feature = "framebuffer"))]
|
||||
/// Fill a whole rectangle with a specific color.
|
||||
pub fn rect_fill(r: Rect, fg_color: Color) {
|
||||
display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into());
|
||||
let r = r.translate(get_offset());
|
||||
let r = r.clamp(constant::screen());
|
||||
|
||||
set_window(r);
|
||||
|
||||
for _ in r.y0..r.y1 {
|
||||
for _ in r.x0..r.x1 {
|
||||
pixeldata(fg_color.into());
|
||||
}
|
||||
}
|
||||
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
pub fn rect_fill(r: Rect, fg_color: Color) {
|
||||
let r = r.translate(get_offset());
|
||||
let r = r.clamp(constant::screen());
|
||||
set_window(r);
|
||||
dma2d_setup_const();
|
||||
unsafe {
|
||||
dma2d_start_const_multiline(fg_color.into(), r.width(), r.height());
|
||||
}
|
||||
dma2d_wait_for_transfer();
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
pub fn rect_stroke(r: Rect, fg_color: Color) {
|
||||
display::bar(r.x0, r.y0, r.width(), 1, fg_color.into());
|
||||
display::bar(r.x0, r.y0 + r.height() - 1, r.width(), 1, fg_color.into());
|
||||
display::bar(r.x0, r.y0, 1, r.height(), fg_color.into());
|
||||
display::bar(r.x0 + r.width() - 1, r.y0, 1, r.height(), fg_color.into());
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(Point::new(r.x0, r.y0), Offset::new(r.width(), 1)),
|
||||
fg_color,
|
||||
);
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(
|
||||
Point::new(r.x0, r.y0 + r.height() - 1),
|
||||
Offset::new(r.width(), 1),
|
||||
),
|
||||
fg_color,
|
||||
);
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(Point::new(r.x0, r.y0), Offset::new(1, r.height())),
|
||||
fg_color,
|
||||
);
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(
|
||||
Point::new(r.x0 + r.width() - 1, r.y0),
|
||||
Offset::new(1, r.height()),
|
||||
),
|
||||
fg_color,
|
||||
);
|
||||
}
|
||||
|
||||
const CORNER_RADIUS: usize = 16;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const CORNER_TABLE: [usize; CORNER_RADIUS * CORNER_RADIUS] = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 9, 12, 14, 15,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 3, 9, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 0, 0, 0, 0, 0, 8, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 0, 0, 0, 3, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 0, 3, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 0, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 0, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
0, 9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
9, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
];
|
||||
|
||||
/// Draw a rectangle with rounded corners.
|
||||
#[cfg(not(feature = "framebuffer"))]
|
||||
pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) {
|
||||
if radius == 1 {
|
||||
rect_fill_rounded1(r, fg_color, bg_color);
|
||||
} else {
|
||||
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
|
||||
display::bar_radius(
|
||||
r.x0,
|
||||
r.y0,
|
||||
r.width(),
|
||||
r.height(),
|
||||
fg_color.into(),
|
||||
bg_color.into(),
|
||||
radius,
|
||||
);
|
||||
|
||||
let color_table = get_color_table(fg_color, bg_color);
|
||||
let area = r.translate(get_offset());
|
||||
let clamped = area.clamp(constant::screen());
|
||||
|
||||
set_window(clamped);
|
||||
|
||||
let radius = radius as i16;
|
||||
let radius_inv = 16 / radius;
|
||||
|
||||
for y in area.y0..area.y1 {
|
||||
for x in area.x0..area.x1 {
|
||||
if x - r.x0 < radius && y - r.y0 < radius {
|
||||
let c = CORNER_TABLE[((x - area.x0) * radius_inv
|
||||
+ (y - area.y0) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize];
|
||||
pixeldata(color_table[c]);
|
||||
} else if x - r.x0 < radius && y - r.y0 >= r.height() - radius {
|
||||
let c = CORNER_TABLE[((x - area.x0) * radius_inv
|
||||
+ (r.height() - 1 - (y - area.y0)) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize];
|
||||
pixeldata(color_table[c]);
|
||||
} else if x - r.x0 >= r.width() - radius && y - r.y0 < radius {
|
||||
let c = CORNER_TABLE[((r.width() - 1 - (x - area.x0)) * radius_inv
|
||||
+ (y - area.y0) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize];
|
||||
pixeldata(color_table[c]);
|
||||
} else if x - r.x0 >= r.width() - radius && y - r.y0 >= r.height() - radius {
|
||||
let c = CORNER_TABLE[((r.width() - 1 - (x - area.x0)) * radius_inv
|
||||
+ (r.height() - 1 - (y - area.y0)) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize];
|
||||
pixeldata(color_table[c]);
|
||||
} else {
|
||||
pixeldata(color_table[15]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
pub fn rect_fill_rounded_buffer(r: Rect, radius: u8, buffer: &mut BufferText) {
|
||||
if r.height() > r.y0 + buffers::TEXT_BUFFER_HEIGHT as i16 || r.x0 + r.width() > WIDTH {
|
||||
return;
|
||||
}
|
||||
|
||||
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
|
||||
|
||||
let radius = radius as i16;
|
||||
let radius_inv = 16 / radius;
|
||||
|
||||
for y in r.y0..r.y1 {
|
||||
for x in r.x0..r.x1 {
|
||||
let c = if x - r.x0 < radius && y - r.y0 < radius {
|
||||
CORNER_TABLE[((x - r.x0) * radius_inv
|
||||
+ (y - r.y0) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize]
|
||||
} else if x - r.x0 < radius && y - r.y0 >= r.height() - radius {
|
||||
CORNER_TABLE[((x - r.x0) * radius_inv
|
||||
+ (r.height() - 1 - (y - r.y0)) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize]
|
||||
} else if x - r.x0 >= r.width() - radius && y - r.y0 < radius {
|
||||
CORNER_TABLE[((r.width() - 1 - (x - r.x0)) * radius_inv
|
||||
+ (y - r.y0) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize]
|
||||
} else if x - r.x0 >= r.width() - radius && y - r.y0 >= r.height() - radius {
|
||||
CORNER_TABLE[((r.width() - 1 - (x - r.x0)) * radius_inv
|
||||
+ (r.height() - 1 - (y - r.y0)) * radius_inv * CORNER_RADIUS as i16)
|
||||
as usize]
|
||||
} else {
|
||||
15usize
|
||||
};
|
||||
let p = y * WIDTH + x;
|
||||
let b = (p / 2) as usize;
|
||||
if p % 2 != 0 {
|
||||
buffer.buffer[b] |= (c << 4) as u8;
|
||||
} else {
|
||||
buffer.buffer[b] |= c as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
/// Draw a rectangle with rounded corners.
|
||||
pub fn rect_fill_rounded(area: Rect, fg_color: Color, bg_color: Color, radius: u8) {
|
||||
let radius = radius as i16;
|
||||
if radius == 1 {
|
||||
rect_fill_rounded1(area, fg_color, bg_color);
|
||||
} else {
|
||||
assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
|
||||
|
||||
let r = area.translate(get_offset());
|
||||
let r = r.clamp(constant::screen());
|
||||
let fb = get_fb_addr();
|
||||
|
||||
rect_fill(r, fg_color);
|
||||
let r_inv = 16 / radius;
|
||||
let color_table = get_color_table(fg_color, bg_color);
|
||||
|
||||
for y in 0..radius {
|
||||
for x in 0..radius {
|
||||
let c = CORNER_TABLE[(x * r_inv + y * r_inv * 16) as usize];
|
||||
pixel(fb, r.x0 + x, r.y0 + y, color_table[c].into());
|
||||
}
|
||||
}
|
||||
for y in 0..radius {
|
||||
for x in 0..radius {
|
||||
let c = CORNER_TABLE[((radius - x - 1) * r_inv + y * r_inv * 16) as usize];
|
||||
pixel(fb, r.x1 - radius + x, r.y0 + y, color_table[c].into());
|
||||
}
|
||||
}
|
||||
for y in 0..radius {
|
||||
for x in 0..radius {
|
||||
let c = CORNER_TABLE[(x * r_inv + (radius - y - 1) * r_inv * 16) as usize];
|
||||
pixel(fb, r.x0 + x, r.y1 - radius + y, color_table[c].into());
|
||||
}
|
||||
}
|
||||
for y in 0..radius {
|
||||
for x in 0..radius {
|
||||
let c = CORNER_TABLE
|
||||
[((radius - x - 1) * r_inv + (radius - y - 1) * r_inv * 16) as usize];
|
||||
pixel(
|
||||
fb,
|
||||
r.x1 - radius + x,
|
||||
r.y1 - radius + y,
|
||||
color_table[c].into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
/// Filling a rectangle with a rounding of 1 pixel - removing the corners.
|
||||
@ -137,7 +334,10 @@ pub fn rect_outline_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u
|
||||
pub fn rect_fill_corners(r: Rect, fg_color: Color) {
|
||||
for p in r.corner_points().iter() {
|
||||
// This draws a 1x1 rectangle at the given point.
|
||||
display::bar(p.x, p.y, 1, 1, fg_color.into());
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(*p, Offset::uniform(1)),
|
||||
fg_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -860,7 +1060,10 @@ pub fn marquee(area: Rect, text: &str, offset: i16, font: Font, fg: Color, bg: C
|
||||
|
||||
pub fn dotted_line(start: Point, width: i16, color: Color, step: i16) {
|
||||
for x in (start.x..width).step_by(step as usize) {
|
||||
display::bar(x, start.y, 1, 1, color.into());
|
||||
rect_fill(
|
||||
Rect::from_top_left_and_size(Point::new(x, start.y), Offset::new(1, 1)),
|
||||
color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
ui::{
|
||||
component::image::Image,
|
||||
constant,
|
||||
display::{get_color_table, get_offset, pixeldata, pixeldata_dirty, set_window},
|
||||
display::{get_offset, pixeldata_dirty, set_window},
|
||||
geometry::{Alignment2D, Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
@ -20,6 +20,12 @@ use crate::{
|
||||
ui::display::process_buffer,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "framebuffer"))]
|
||||
use crate::ui::display::{get_color_table, pixeldata};
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
use crate::trezorhal::{buffers::BufferLine4bpp, dma2d::dma2d_setup_4bpp};
|
||||
|
||||
use super::Color;
|
||||
|
||||
const TOIF_HEADER_LENGTH: usize = 12;
|
||||
@ -28,6 +34,7 @@ pub fn render_icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color)
|
||||
render_toif(&icon.toif, center, fg_color, bg_color);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "framebuffer"))]
|
||||
pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color) {
|
||||
let r = Rect::from_center_and_size(center, toif.size());
|
||||
let area = r.translate(get_offset());
|
||||
@ -63,6 +70,34 @@ pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color)
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
#[cfg(feature = "framebuffer")]
|
||||
pub fn render_toif(toif: &Toif, center: Point, fg_color: Color, bg_color: Color) {
|
||||
let r = Rect::from_center_and_size(center, toif.size());
|
||||
let area = r.translate(get_offset());
|
||||
|
||||
set_window(area);
|
||||
|
||||
let mut b1 = BufferLine4bpp::get_cleared();
|
||||
let mut b2 = BufferLine4bpp::get_cleared();
|
||||
|
||||
let mut window = [0; UZLIB_WINDOW_SIZE];
|
||||
let mut ctx = toif.decompression_context(Some(&mut window));
|
||||
|
||||
dma2d_setup_4bpp(fg_color.into(), bg_color.into());
|
||||
|
||||
for y in area.y0..area.y1 {
|
||||
let img_buffer_used = if y % 2 == 0 { &mut b1 } else { &mut b2 };
|
||||
|
||||
unwrap!(ctx.uncompress(&mut (&mut img_buffer_used.buffer)[0..(area.width() / 2) as usize]));
|
||||
|
||||
dma2d_wait_for_transfer();
|
||||
unsafe { dma2d_start(&img_buffer_used.buffer, area.width()) };
|
||||
}
|
||||
|
||||
dma2d_wait_for_transfer();
|
||||
pixeldata_dirty();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn display_image(
|
||||
x: cty::int16_t,
|
||||
|
@ -2,7 +2,6 @@ use crate::{
|
||||
trezorhal::{
|
||||
buffers::{BufferBlurring, BufferJpeg, BufferLine16bpp, BufferLine4bpp, BufferText},
|
||||
display,
|
||||
display::bar_radius_buffer,
|
||||
dma2d::{dma2d_setup_4bpp_over_16bpp, dma2d_start_blend, dma2d_wait_for_transfer},
|
||||
uzlib::UzlibContext,
|
||||
},
|
||||
@ -10,7 +9,7 @@ use crate::{
|
||||
component::text::TextStyle,
|
||||
constant::{screen, HEIGHT, WIDTH},
|
||||
display::{
|
||||
position_buffer, set_window,
|
||||
position_buffer, rect_fill_rounded_buffer, set_window,
|
||||
tjpgd::{BufferInput, BufferOutput, JDEC},
|
||||
Color, Icon,
|
||||
},
|
||||
@ -645,11 +644,11 @@ pub fn homescreen(
|
||||
|
||||
let mut next_text_idx = 0;
|
||||
let mut text_info = if let Some(notification) = notification {
|
||||
bar_radius_buffer(
|
||||
NOTIFICATION_BORDER,
|
||||
0,
|
||||
WIDTH - NOTIFICATION_BORDER * 2,
|
||||
NOTIFICATION_HEIGHT,
|
||||
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,
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ mod fido_icons;
|
||||
mod error;
|
||||
mod frame;
|
||||
mod hold_to_confirm;
|
||||
#[cfg(feature = "dma2d")]
|
||||
#[cfg(feature = "micropython")]
|
||||
mod homescreen;
|
||||
mod horizontal_page;
|
||||
mod keyboard;
|
||||
@ -32,7 +32,7 @@ pub use error::ErrorScreen;
|
||||
pub use fido::{FidoConfirm, FidoMsg};
|
||||
pub use frame::{Frame, FrameMsg};
|
||||
pub use hold_to_confirm::{HoldToConfirm, HoldToConfirmMsg};
|
||||
#[cfg(feature = "dma2d")]
|
||||
#[cfg(feature = "micropython")]
|
||||
pub use homescreen::{Homescreen, HomescreenMsg, Lockscreen};
|
||||
pub use horizontal_page::HorizontalPage;
|
||||
pub use keyboard::{
|
||||
|
@ -33,6 +33,8 @@ void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color);
|
||||
|
||||
void dma2d_start(uint8_t* in_addr, uint8_t* out_addr, int32_t pixels);
|
||||
void dma2d_start_const(uint16_t color, uint8_t* out_addr, int32_t pixels);
|
||||
void dma2d_start_const_multiline(uint16_t color, uint8_t* out_addr,
|
||||
int32_t width, int32_t height);
|
||||
void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
|
||||
uint8_t* out_addr, int32_t pixels);
|
||||
|
||||
|
@ -43,29 +43,30 @@ static int DISPLAY_ORIENTATION = -1;
|
||||
// this is just for compatibility with DMA2D using algorithms
|
||||
uint8_t *const DISPLAY_DATA_ADDRESS = 0;
|
||||
|
||||
uint16_t display_index_x = 0;
|
||||
uint16_t display_index_y = 0;
|
||||
uint16_t display_window_x0 = 0;
|
||||
uint16_t display_window_y0 = MAX_DISPLAY_RESX - 1;
|
||||
uint16_t display_window_x1 = 0;
|
||||
uint16_t display_window_y1 = MAX_DISPLAY_RESY - 1;
|
||||
uint16_t cursor_x = 0;
|
||||
uint16_t cursor_y = 0;
|
||||
uint16_t window_x0 = 0;
|
||||
uint16_t window_y0 = MAX_DISPLAY_RESX - 1;
|
||||
uint16_t window_x1 = 0;
|
||||
uint16_t window_y1 = MAX_DISPLAY_RESY - 1;
|
||||
|
||||
void display_pixeldata(uint16_t c) {
|
||||
((uint16_t *)LCD_FRAME_BUFFER)[(display_index_y * MAX_DISPLAY_RESX) +
|
||||
display_index_x] = c;
|
||||
((uint16_t *)LCD_FRAME_BUFFER)[(cursor_y * MAX_DISPLAY_RESX) + cursor_x] = c;
|
||||
|
||||
display_index_x++;
|
||||
cursor_x++;
|
||||
|
||||
if (display_index_x > display_window_x1) {
|
||||
display_index_x = display_window_x0;
|
||||
display_index_y++;
|
||||
if (cursor_x > window_x1) {
|
||||
cursor_x = window_x0;
|
||||
cursor_y++;
|
||||
|
||||
if (display_index_y > display_window_y1) {
|
||||
display_index_y = display_window_y0;
|
||||
if (cursor_y > window_y1) {
|
||||
cursor_y = window_y0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display_pixeldata_dirty(void) {}
|
||||
|
||||
void display_reset_state() {}
|
||||
|
||||
static void __attribute__((unused)) display_sleep(void) {}
|
||||
@ -192,12 +193,12 @@ void BSP_LCD_SetLayerAddress_NoReload(uint32_t LayerIndex, uint32_t Address) {
|
||||
// static struct { uint16_t x, y; } BUFFER_OFFSET;
|
||||
|
||||
void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
||||
display_window_x0 = x0;
|
||||
display_window_x1 = x1;
|
||||
display_window_y0 = y0;
|
||||
display_window_y1 = y1;
|
||||
display_index_x = x0;
|
||||
display_index_y = y0;
|
||||
window_x0 = x0;
|
||||
window_x1 = x1;
|
||||
window_y0 = y0;
|
||||
window_y1 = y1;
|
||||
cursor_x = x0;
|
||||
cursor_y = y0;
|
||||
|
||||
// /* Reconfigure the layer size */
|
||||
// HAL_LTDC_SetWindowSize_NoReload(&LtdcHandler, x1-x0 + 1, y1-y0 + 1, 0);
|
||||
@ -371,3 +372,46 @@ void display_sync(void) {}
|
||||
const char *display_save(const char *prefix) { return NULL; }
|
||||
|
||||
void display_clear_save(void) {}
|
||||
|
||||
void display_efficient_clear(void) {
|
||||
memzero((void *)LCD_FRAME_BUFFER, 153600);
|
||||
}
|
||||
|
||||
uint8_t *display_get_wr_addr(void) {
|
||||
uint32_t address = LCD_FRAME_BUFFER;
|
||||
/* Get the rectangle start address */
|
||||
address = (address + (2 * ((cursor_y)*MAX_DISPLAY_RESX + (cursor_x))));
|
||||
|
||||
return (uint8_t *)address;
|
||||
}
|
||||
|
||||
uint32_t *display_get_fb_addr(void) { return (uint32_t *)LCD_FRAME_BUFFER; }
|
||||
|
||||
uint16_t display_get_window_width(void) { return window_x1 - window_x0 + 1; }
|
||||
|
||||
uint16_t display_get_window_height(void) { return window_y1 - window_y0 + 1; }
|
||||
|
||||
void display_shift_window(uint16_t pixels) {
|
||||
uint16_t w = display_get_window_width();
|
||||
uint16_t h = display_get_window_height();
|
||||
|
||||
uint16_t line_rem = w - (cursor_x - window_x0);
|
||||
|
||||
if (pixels < line_rem) {
|
||||
cursor_x += pixels;
|
||||
return;
|
||||
}
|
||||
|
||||
// start of next line
|
||||
pixels = pixels - line_rem;
|
||||
cursor_x = window_x0;
|
||||
cursor_y++;
|
||||
|
||||
// add the rest of pixels
|
||||
cursor_y = window_y0 + (((cursor_y - window_y0) + (pixels / w)) % h);
|
||||
cursor_x += pixels % w;
|
||||
}
|
||||
|
||||
uint16_t display_get_window_offset(void) {
|
||||
return MAX_DISPLAY_RESX - display_get_window_width();
|
||||
}
|
||||
|
@ -5,7 +5,19 @@
|
||||
#include STM32_HAL_H
|
||||
|
||||
#define TREZOR_FONT_BPP 4
|
||||
#define DISPLAY_FRAMEBUFFER_WIDTH MAX_DISPLAY_RESX
|
||||
#define DISPLAY_FRAMEBUFFER_HEIGHT MAX_DISPLAY_RESY
|
||||
#define DISPLAY_FRAMEBUFFER_OFFSET_X 0
|
||||
#define DISPLAY_FRAMEBUFFER_OFFSET_Y 0
|
||||
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565
|
||||
#define DISPLAY_EFFICIENT_CLEAR 1
|
||||
|
||||
extern uint8_t *const DISPLAY_DATA_ADDRESS;
|
||||
extern uint8_t* const DISPLAY_DATA_ADDRESS;
|
||||
|
||||
static inline void display_pixel(uint8_t* fb, int16_t x, int16_t y,
|
||||
uint16_t color) {
|
||||
uint32_t p = 2 * (y * DISPLAY_FRAMEBUFFER_WIDTH + x);
|
||||
*((uint16_t*)(fb + p)) = color;
|
||||
}
|
||||
|
||||
#endif //_LTDC_H
|
||||
|
@ -70,6 +70,8 @@ static int DISPLAY_ORIENTATION = -1;
|
||||
|
||||
void display_pixeldata(uint16_t c) { PIXELDATA(c); }
|
||||
|
||||
void display_pixeldata_dirty(void) {}
|
||||
|
||||
static uint32_t read_display_id(uint8_t command) {
|
||||
volatile uint8_t c = 0;
|
||||
uint32_t id = 0;
|
||||
@ -470,3 +472,9 @@ void display_set_big_endian(void) {
|
||||
const char *display_save(const char *prefix) { return NULL; }
|
||||
|
||||
void display_clear_save(void) {}
|
||||
|
||||
uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; }
|
||||
|
||||
uint16_t display_get_window_offset(void) { return 0; }
|
||||
|
||||
void display_shift_window(uint16_t pixels) {}
|
||||
|
@ -7,6 +7,7 @@
|
||||
// ILI9341V, GC9307 and ST7789V drivers support 240px x 320px display resolution
|
||||
#define MAX_DISPLAY_RESX 240
|
||||
#define MAX_DISPLAY_RESY 320
|
||||
#define DISPLAY_COLOR_MODE DMA2D_OUTPUT_RGB565
|
||||
#define TREZOR_FONT_BPP 4
|
||||
|
||||
#ifdef USE_DISP_I8080_16BIT_DW
|
||||
|
@ -33,9 +33,6 @@
|
||||
(1 << DISPLAY_MEMORY_PIN)))))
|
||||
#define DATA(X) (ADDR) = (X)
|
||||
|
||||
// noop on TR as we don't need to push data to display
|
||||
#define PIXELDATA_DIRTY()
|
||||
|
||||
static int DISPLAY_BACKLIGHT = -1;
|
||||
static int DISPLAY_ORIENTATION = -1;
|
||||
struct {
|
||||
@ -97,7 +94,7 @@ void display_pixeldata(uint16_t c) {
|
||||
}
|
||||
}
|
||||
|
||||
#define PIXELDATA(c) display_pixeldata(c)
|
||||
void display_pixeldata_dirty(void) {}
|
||||
|
||||
void display_reset_state(void) {
|
||||
memzero(DISPLAY_STATE.RAM, sizeof(DISPLAY_STATE.RAM));
|
||||
|
@ -92,11 +92,9 @@ void display_pixeldata(uint16_t c) {
|
||||
}
|
||||
}
|
||||
|
||||
#define PIXELDATA(c) display_pixeldata(c)
|
||||
|
||||
void display_reset_state() {}
|
||||
|
||||
void pixeldata_dirty(void) { pixeldata_dirty_flag = true; }
|
||||
void display_pixeldata_dirty(void) { pixeldata_dirty_flag = true; }
|
||||
|
||||
void display_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
||||
PIXELWINDOW.start.x = x0;
|
||||
|
@ -7,7 +7,4 @@
|
||||
#define DISPLAY_RESY 64
|
||||
#define TREZOR_FONT_BPP 1
|
||||
|
||||
void pixeldata_dirty(void);
|
||||
#define PIXELDATA_DIRTY() pixeldata_dirty();
|
||||
|
||||
#endif //_VG_2864KSWEG01_H
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "dma2d.h"
|
||||
#include "colors.h"
|
||||
#include STM32_HAL_H
|
||||
#include "display_interface.h"
|
||||
|
||||
typedef enum {
|
||||
DMA2D_LAYER_FG = 1,
|
||||
@ -27,12 +28,14 @@ typedef enum {
|
||||
} dma2d_layer_t;
|
||||
|
||||
static DMA2D_HandleTypeDef dma2d_handle = {0};
|
||||
static uint16_t current_width = 0;
|
||||
static uint16_t current_height = 0;
|
||||
|
||||
void dma2d_init(void) {
|
||||
__HAL_RCC_DMA2D_CLK_ENABLE();
|
||||
|
||||
dma2d_handle.Instance = (DMA2D_TypeDef*)DMA2D_BASE;
|
||||
dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565;
|
||||
dma2d_handle.Init.ColorMode = DISPLAY_COLOR_MODE;
|
||||
dma2d_handle.Init.OutputOffset = 0;
|
||||
}
|
||||
|
||||
@ -61,11 +64,13 @@ static void dma2d_init_clut(uint16_t fg, uint16_t bg, dma2d_layer_t layer) {
|
||||
|
||||
void dma2d_setup_const(void) {
|
||||
dma2d_handle.Init.Mode = DMA2D_R2M;
|
||||
dma2d_handle.Init.OutputOffset = display_get_window_offset();
|
||||
HAL_DMA2D_Init(&dma2d_handle);
|
||||
}
|
||||
|
||||
void dma2d_setup_4bpp(uint16_t fg_color, uint16_t bg_color) {
|
||||
dma2d_handle.Init.Mode = DMA2D_M2M_PFC;
|
||||
dma2d_handle.Init.OutputOffset = display_get_window_offset();
|
||||
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_L4;
|
||||
dma2d_handle.LayerCfg[1].InputOffset = 0;
|
||||
dma2d_handle.LayerCfg[1].AlphaMode = 0;
|
||||
@ -79,6 +84,7 @@ void dma2d_setup_4bpp(uint16_t fg_color, uint16_t bg_color) {
|
||||
|
||||
void dma2d_setup_16bpp(void) {
|
||||
dma2d_handle.Init.Mode = DMA2D_M2M_PFC;
|
||||
dma2d_handle.Init.OutputOffset = display_get_window_offset();
|
||||
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565;
|
||||
dma2d_handle.LayerCfg[1].InputOffset = 0;
|
||||
dma2d_handle.LayerCfg[1].AlphaMode = 0;
|
||||
@ -90,6 +96,7 @@ void dma2d_setup_16bpp(void) {
|
||||
|
||||
void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color) {
|
||||
dma2d_handle.Init.Mode = DMA2D_M2M_BLEND;
|
||||
dma2d_handle.Init.OutputOffset = display_get_window_offset();
|
||||
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4;
|
||||
dma2d_handle.LayerCfg[1].InputOffset = 0;
|
||||
dma2d_handle.LayerCfg[1].AlphaMode = 0;
|
||||
@ -109,6 +116,7 @@ void dma2d_setup_4bpp_over_16bpp(uint16_t overlay_color) {
|
||||
void dma2d_setup_4bpp_over_4bpp(uint16_t fg_color, uint16_t bg_color,
|
||||
uint16_t overlay_color) {
|
||||
dma2d_handle.Init.Mode = DMA2D_M2M_BLEND;
|
||||
dma2d_handle.Init.OutputOffset = display_get_window_offset();
|
||||
dma2d_handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_A4;
|
||||
dma2d_handle.LayerCfg[1].InputOffset = 0;
|
||||
dma2d_handle.LayerCfg[1].AlphaMode = 0;
|
||||
@ -127,17 +135,31 @@ void dma2d_setup_4bpp_over_4bpp(uint16_t fg_color, uint16_t bg_color,
|
||||
}
|
||||
|
||||
void dma2d_start(uint8_t* in_addr, uint8_t* out_addr, int32_t pixels) {
|
||||
current_width = pixels;
|
||||
current_height = 1;
|
||||
HAL_DMA2D_Start(&dma2d_handle, (uint32_t)in_addr, (uint32_t)out_addr, pixels,
|
||||
1);
|
||||
}
|
||||
|
||||
void dma2d_start_const(uint16_t color, uint8_t* out_addr, int32_t pixels) {
|
||||
current_width = pixels;
|
||||
current_height = 1;
|
||||
HAL_DMA2D_Start(&dma2d_handle, rgb565_to_rgb888(color), (uint32_t)out_addr,
|
||||
pixels, 1);
|
||||
}
|
||||
|
||||
void dma2d_start_const_multiline(uint16_t color, uint8_t* out_addr,
|
||||
int32_t width, int32_t height) {
|
||||
current_width = width;
|
||||
current_height = height;
|
||||
HAL_DMA2D_Start(&dma2d_handle, rgb565_to_rgb888(color), (uint32_t)out_addr,
|
||||
width, height);
|
||||
}
|
||||
|
||||
void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
|
||||
uint8_t* out_addr, int32_t pixels) {
|
||||
current_width = pixels;
|
||||
current_height = 1;
|
||||
HAL_DMA2D_BlendingStart(&dma2d_handle, (uint32_t)overlay_addr,
|
||||
(uint32_t)bg_addr, (uint32_t)out_addr, pixels, 1);
|
||||
}
|
||||
@ -145,4 +167,7 @@ void dma2d_start_blend(uint8_t* overlay_addr, uint8_t* bg_addr,
|
||||
void dma2d_wait_for_transfer(void) {
|
||||
while (HAL_DMA2D_PollForTransfer(&dma2d_handle, 10) != HAL_OK)
|
||||
;
|
||||
display_shift_window(current_width * current_height);
|
||||
current_width = 0;
|
||||
current_height = 0;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ void display_pixeldata(pixel_color c) {
|
||||
}
|
||||
}
|
||||
|
||||
#define PIXELDATA(c) display_pixeldata(c)
|
||||
void display_pixeldata_dirty(void) {}
|
||||
|
||||
void display_reset_state() {}
|
||||
|
||||
@ -332,3 +332,5 @@ void display_clear_save(void) {
|
||||
SDL_FreeSurface(PREV_SAVED);
|
||||
PREV_SAVED = NULL;
|
||||
}
|
||||
|
||||
uint8_t *display_get_wr_addr(void) { return (uint8_t *)DISPLAY_DATA_ADDRESS; }
|
||||
|
@ -38,11 +38,18 @@ def configure(
|
||||
]
|
||||
sources += [f"embed/trezorhal/stm32f4/displays/{display}"]
|
||||
sources += ["embed/trezorhal/stm32f4/displays/ili9341_spi.c"]
|
||||
sources += ["embed/trezorhal/stm32f4/dma.c"]
|
||||
sources += ["embed/trezorhal/stm32f4/dma2d.c"]
|
||||
sources += [
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma2d.c"
|
||||
]
|
||||
sources += ["embed/trezorhal/stm32f4/sdram.c"]
|
||||
sources += [
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c"
|
||||
]
|
||||
defines += ["USE_DMA2D"]
|
||||
defines += ["FRAMEBUFFER"]
|
||||
features_available.append("dma2d")
|
||||
features_available.append("framebuffer")
|
||||
|
||||
if "input" in features_wanted:
|
||||
sources += ["embed/trezorhal/stm32f4/i2c.c"]
|
||||
@ -50,11 +57,6 @@ def configure(
|
||||
sources += ["embed/lib/touch.c"]
|
||||
features_available.append("touch")
|
||||
|
||||
if "dma2d" in features_wanted:
|
||||
defines += ["USE_DMA2D"]
|
||||
sources += ["embed/lib/dma2d_emul.c"]
|
||||
features_available.append("dma2d")
|
||||
|
||||
if "usb" in features_wanted:
|
||||
sources += [
|
||||
"embed/trezorhal/stm32f4/usb.c",
|
||||
|
Loading…
Reference in New Issue
Block a user