mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-27 02:12:35 +00:00
feat(core): allow JPEG image to be decoded as Mono8 bitmap
[no changelog]
This commit is contained in:
parent
622fa05e48
commit
db057ff6ae
@ -33,6 +33,9 @@
|
|||||||
// Maximum size of the RGBA8888 buffer for a slice.
|
// Maximum size of the RGBA8888 buffer for a slice.
|
||||||
#define JPEGDEC_RGBA8888_BUFFER_SIZE (JPEGDEC_MAX_SLICE_BLOCKS * 8 * 8 * 4)
|
#define JPEGDEC_RGBA8888_BUFFER_SIZE (JPEGDEC_MAX_SLICE_BLOCKS * 8 * 8 * 4)
|
||||||
|
|
||||||
|
// Maximum size of the MONO8 buffer for a slice
|
||||||
|
#define JPEGDEC_MONO8_BUFFER_SIZE (JPEGDEC_MAX_SLICE_BLOCKS * 8 * 8)
|
||||||
|
|
||||||
typedef struct jpegdec jpegdec_t;
|
typedef struct jpegdec jpegdec_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -43,7 +46,7 @@ typedef enum {
|
|||||||
// (jpegdec_get_info can be called to get the image info)
|
// (jpegdec_get_info can be called to get the image info)
|
||||||
JPEGDEC_STATE_INFO_READY,
|
JPEGDEC_STATE_INFO_READY,
|
||||||
// Decoded slice is ready
|
// Decoded slice is ready
|
||||||
// (jpegdec_get_slice_rgba8888 can be called to get the slice data)
|
// (jpegdec_get_slice_xxx can be called to get the slice data)
|
||||||
JPEGDEC_STATE_SLICE_READY,
|
JPEGDEC_STATE_SLICE_READY,
|
||||||
// Decoding is finished
|
// Decoding is finished
|
||||||
JPEGDEC_STATE_FINISHED,
|
JPEGDEC_STATE_FINISHED,
|
||||||
@ -125,3 +128,12 @@ bool jpegdec_get_info(jpegdec_image_t* info);
|
|||||||
// Can be called immediately after `jpegdec_process` returns
|
// Can be called immediately after `jpegdec_process` returns
|
||||||
// `JPEGDEC_STATE_SLICE_READY`.
|
// `JPEGDEC_STATE_SLICE_READY`.
|
||||||
bool jpegdec_get_slice_rgba8888(uint32_t* rgba8888, jpegdec_slice_t* slice);
|
bool jpegdec_get_slice_rgba8888(uint32_t* rgba8888, jpegdec_slice_t* slice);
|
||||||
|
|
||||||
|
// Copy the last decoded slice to the buffer
|
||||||
|
//
|
||||||
|
// `mono8` must be a buffer of at least `JPEGDEC_MONO8_BUFFER_SIZE`
|
||||||
|
// bytes and must be aligned to 4 bytes.
|
||||||
|
//
|
||||||
|
// Can be called immediately after `jpegdec_process` returns
|
||||||
|
// `JPEGDEC_STATE_SLICE_READY`.
|
||||||
|
bool jpegdec_get_slice_mono8(uint32_t* mono8, jpegdec_slice_t* slice);
|
||||||
|
@ -463,4 +463,118 @@ bool jpegdec_get_slice_rgba8888(uint32_t *rgba8888, jpegdec_slice_t *slice) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize DMA base copy for fast copying of 8x8 blocks
|
||||||
|
//
|
||||||
|
// 'dst_stride' is the number of bytes between the start of two consecutive
|
||||||
|
// rows in the destination buffer.
|
||||||
|
static void fast_copy_init(DMA_HandleTypeDef *hdma, size_t dst_stride) {
|
||||||
|
hdma->Instance = GPDMA1_Channel13;
|
||||||
|
hdma->Init.Request = GPDMA1_REQUEST_HASH_IN;
|
||||||
|
hdma->Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||||
|
hdma->Init.Direction = DMA_MEMORY_TO_MEMORY;
|
||||||
|
hdma->Init.SrcInc = DMA_SINC_INCREMENTED;
|
||||||
|
hdma->Init.DestInc = DMA_DINC_INCREMENTED;
|
||||||
|
hdma->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
|
||||||
|
hdma->Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
|
||||||
|
hdma->Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
|
||||||
|
hdma->Init.SrcBurstLength = 2;
|
||||||
|
hdma->Init.DestBurstLength = 2;
|
||||||
|
hdma->Init.TransferAllocatedPort =
|
||||||
|
DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0;
|
||||||
|
hdma->Init.TransferEventMode = DMA_TCEM_REPEATED_BLOCK_TRANSFER;
|
||||||
|
hdma->Init.Mode = DMA_NORMAL;
|
||||||
|
HAL_DMA_Init(hdma);
|
||||||
|
HAL_DMA_ConfigChannelAttributes(hdma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
|
||||||
|
DMA_CHANNEL_SRC_SEC |
|
||||||
|
DMA_CHANNEL_DEST_SEC);
|
||||||
|
|
||||||
|
DMA_RepeatBlockConfTypeDef rep = {0};
|
||||||
|
|
||||||
|
rep.DestAddrOffset = dst_stride - 8;
|
||||||
|
rep.RepeatCount = 1;
|
||||||
|
HAL_DMAEx_ConfigRepeatBlock(hdma, &rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate a fast copy of an 8x8 block from 'src' to 'dst'
|
||||||
|
//
|
||||||
|
// `src` is expected to be a pointer to the start of an 8x8 block.
|
||||||
|
// `dst` is expected to be a pointer to destination bitmap buffer
|
||||||
|
static inline void fast_copy_block(DMA_HandleTypeDef *hdma, uint8_t *dst,
|
||||||
|
uint8_t *src) {
|
||||||
|
while ((hdma->Instance->CSR & DMA_FLAG_IDLE) == 0)
|
||||||
|
;
|
||||||
|
|
||||||
|
hdma->Lock = 0;
|
||||||
|
hdma->State = HAL_DMA_STATE_READY;
|
||||||
|
|
||||||
|
HAL_DMA_Start(hdma, (uint32_t)src, (uint32_t)dst, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinitialize the DMA base copy
|
||||||
|
static inline void fast_copy_deinit(DMA_HandleTypeDef *hdma) {
|
||||||
|
while ((hdma->Instance->CSR & DMA_FLAG_IDLE) == 0)
|
||||||
|
;
|
||||||
|
|
||||||
|
hdma->Lock = 0;
|
||||||
|
hdma->State = HAL_DMA_STATE_READY;
|
||||||
|
|
||||||
|
HAL_DMA_DeInit(hdma);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool jpegdec_get_slice_mono8(uint32_t *mono8, jpegdec_slice_t *slice) {
|
||||||
|
jpegdec_t *dec = &g_jpegdec;
|
||||||
|
|
||||||
|
if (!dec->inuse) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dec->state != JPEGDEC_STATE_SLICE_READY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_ALIGNED((uint32_t)mono8, 4)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice->width = dec->slice_width;
|
||||||
|
slice->height = dec->slice_height;
|
||||||
|
slice->x = dec->slice_x;
|
||||||
|
slice->y = dec->slice_y;
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
switch (dec->image.format) {
|
||||||
|
case JPEGDEC_IMAGE_YCBCR420:
|
||||||
|
// Not implemented
|
||||||
|
break;
|
||||||
|
case JPEGDEC_IMAGE_YCBCR422:
|
||||||
|
// Not implemented
|
||||||
|
break;
|
||||||
|
case JPEGDEC_IMAGE_YCBCR444:
|
||||||
|
// Not implemented
|
||||||
|
break;
|
||||||
|
case JPEGDEC_IMAGE_GRAYSCALE: {
|
||||||
|
static DMA_HandleTypeDef hdma = {0};
|
||||||
|
fast_copy_init(&hdma, dec->slice_width);
|
||||||
|
uint8_t *src = (uint8_t *)dec->ycbcr_buffer;
|
||||||
|
for (int y = 0; y < dec->slice_height; y += 8) {
|
||||||
|
for (int x = 0; x < dec->slice_width; x += 8) {
|
||||||
|
uint8_t *dst = (uint8_t *)mono8 + y * dec->slice_width + x;
|
||||||
|
fast_copy_block(&hdma, dst, src);
|
||||||
|
src += 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_copy_deinit(&hdma);
|
||||||
|
result = true;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // KERNEL_MODE
|
#endif // KERNEL_MODE
|
||||||
|
@ -416,6 +416,7 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_function("haptic_play_custom")
|
.allowlist_function("haptic_play_custom")
|
||||||
// jpegdec
|
// jpegdec
|
||||||
.allowlist_var("JPEGDEC_RGBA8888_BUFFER_SIZE")
|
.allowlist_var("JPEGDEC_RGBA8888_BUFFER_SIZE")
|
||||||
|
.allowlist_var("JPEGDEC_MONO8_BUFFER_SIZE")
|
||||||
.allowlist_type("jpegdec_state_t")
|
.allowlist_type("jpegdec_state_t")
|
||||||
.allowlist_type("jpegdec_image_t")
|
.allowlist_type("jpegdec_image_t")
|
||||||
.allowlist_type("jpegdec_image_format_t")
|
.allowlist_type("jpegdec_image_format_t")
|
||||||
@ -424,7 +425,8 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_function("jpegdec_close")
|
.allowlist_function("jpegdec_close")
|
||||||
.allowlist_function("jpegdec_process")
|
.allowlist_function("jpegdec_process")
|
||||||
.allowlist_function("jpegdec_get_info")
|
.allowlist_function("jpegdec_get_info")
|
||||||
.allowlist_function("jpegdec_get_slice_rgba8888");
|
.allowlist_function("jpegdec_get_slice_rgba8888")
|
||||||
|
.allowlist_function("jpegdec_get_slice_mono8");
|
||||||
|
|
||||||
// Write the bindings to a file in the OUR_DIR.
|
// Write the bindings to a file in the OUR_DIR.
|
||||||
bindings
|
bindings
|
||||||
|
@ -9,6 +9,7 @@ use crate::io::BinaryData;
|
|||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
pub const RGBA8888_BUFFER_SIZE: usize = ffi::JPEGDEC_RGBA8888_BUFFER_SIZE as _;
|
pub const RGBA8888_BUFFER_SIZE: usize = ffi::JPEGDEC_RGBA8888_BUFFER_SIZE as _;
|
||||||
|
pub const MONO8_BUFFER_SIZE: usize = ffi::JPEGDEC_MONO8_BUFFER_SIZE as _;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
|
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
|
||||||
enum JpegDecState {
|
enum JpegDecState {
|
||||||
@ -129,6 +130,33 @@ impl<'a> JpegDecoder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decodes the JPEG image and calls the output function for each slice.
|
||||||
|
/// The function decodes only the Y component of the image, so
|
||||||
|
/// the output is always grayscale (BitmapFormat::MONO8).
|
||||||
|
/// Requires a temporary buffer of size `RGBA8888_BUFFER_SIZE`.
|
||||||
|
/// The output function should return `true` to continue decoding or `false`
|
||||||
|
/// to stop. Returns `Ok(())` if the decoding was successful or
|
||||||
|
/// `Err(())` if an error occurred.
|
||||||
|
pub fn decode_mono8(
|
||||||
|
&mut self,
|
||||||
|
buff: &mut [u8],
|
||||||
|
output: &mut dyn FnMut(Rect, BitmapView) -> bool,
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
loop {
|
||||||
|
match self.read_input() {
|
||||||
|
JpegDecState::SliceReady => {
|
||||||
|
if !self.write_output_mono8(buff, output) {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
JpegDecState::Finished => break,
|
||||||
|
JpegDecState::NeedData => {}
|
||||||
|
_ => return Err(()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn read_input(&mut self) -> JpegDecState {
|
fn read_input(&mut self) -> JpegDecState {
|
||||||
if self.buff_pos == self.buff_len {
|
if self.buff_pos == self.buff_len {
|
||||||
self.buff_len = self.jpeg.read(self.jpeg_pos, &mut self.buff);
|
self.buff_len = self.jpeg.read(self.jpeg_pos, &mut self.buff);
|
||||||
@ -173,8 +201,8 @@ impl<'a> JpegDecoder<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - `rgba_u32` is a valid pointer to a mutable buffer of u32 of length at
|
// - `rgba_u32` is a valid pointer to a mutable buffer of length at least
|
||||||
// least `RGBA8888_BUFFER_SIZE`
|
// `RGBA8888_BUFFER_SIZE` aligned to u32
|
||||||
// - `slice` is a valid pointer to a mutable `jpegdec_slice_t`
|
// - `slice` is a valid pointer to a mutable `jpegdec_slice_t`
|
||||||
// - `jpegdec_get_slice_rgba8888` doesn't retain the pointers to the data for
|
// - `jpegdec_get_slice_rgba8888` doesn't retain the pointers to the data for
|
||||||
// later use
|
// later use
|
||||||
@ -201,4 +229,52 @@ impl<'a> JpegDecoder<'a> {
|
|||||||
|
|
||||||
output(r, view)
|
output(r, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_output_mono8(
|
||||||
|
&self,
|
||||||
|
buff: &mut [u8],
|
||||||
|
output: &mut dyn FnMut(Rect, BitmapView) -> bool,
|
||||||
|
) -> bool {
|
||||||
|
// SAFETY:
|
||||||
|
// - after aligning the buffer to u32, the we check the
|
||||||
|
// length of the buffer to be at least `MONO8_BUFFER_SIZE`
|
||||||
|
let buff_u32 = unsafe { buff.align_to_mut::<u32>().1 };
|
||||||
|
assert!(buff_u32.len() * 4 >= MONO8_BUFFER_SIZE);
|
||||||
|
|
||||||
|
let mut slice = ffi::jpegdec_slice_t {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - `buff` is a valid pointer to a mutable buffer of length at least
|
||||||
|
// `MONO8_BUFFER_SIZE` bytes aligned to u32
|
||||||
|
// - `slice` is a valid pointer to a mutable `jpegdec_slice_t`
|
||||||
|
// - `jpegdec_get_slice_yonly` doesn't retain the pointers to the data for
|
||||||
|
// later use
|
||||||
|
unsafe { ffi::jpegdec_get_slice_mono8(buff_u32.as_mut_ptr(), &mut slice) };
|
||||||
|
|
||||||
|
let r = Rect::from_top_left_and_size(
|
||||||
|
Point::new(slice.x, slice.y),
|
||||||
|
Offset::new(slice.width, slice.height),
|
||||||
|
);
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// - reinterpreting &[u32] to &[u8] is safe
|
||||||
|
let buff_u8 = unsafe { buff.align_to::<u8>().1 };
|
||||||
|
|
||||||
|
let bitmap = unwrap!(Bitmap::new(
|
||||||
|
BitmapFormat::MONO8,
|
||||||
|
None,
|
||||||
|
r.size(),
|
||||||
|
None,
|
||||||
|
buff_u8
|
||||||
|
));
|
||||||
|
|
||||||
|
let view = BitmapView::new(&bitmap);
|
||||||
|
|
||||||
|
output(r, view)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,6 +779,12 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
|
|||||||
(void *)args[0], (jpegdec_slice_t *)args[1]);
|
(void *)args[0], (jpegdec_slice_t *)args[1]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SYSCALL_JPEGDEC_GET_SLICE_MONO8: {
|
||||||
|
args[0] = jpegdec_get_slice_mono8__verified((void *)args[0],
|
||||||
|
(jpegdec_slice_t *)args[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif // USE_HW_JPEG_DECODER
|
#endif // USE_HW_JPEG_DECODER
|
||||||
|
|
||||||
#ifdef USE_DMA2D
|
#ifdef USE_DMA2D
|
||||||
|
@ -157,6 +157,7 @@ typedef enum {
|
|||||||
SYSCALL_JPEGDEC_PROCESS,
|
SYSCALL_JPEGDEC_PROCESS,
|
||||||
SYSCALL_JPEGDEC_GET_INFO,
|
SYSCALL_JPEGDEC_GET_INFO,
|
||||||
SYSCALL_JPEGDEC_GET_SLICE_RGBA8888,
|
SYSCALL_JPEGDEC_GET_SLICE_RGBA8888,
|
||||||
|
SYSCALL_JPEGDEC_GET_SLICE_MONO8,
|
||||||
|
|
||||||
SYSCALL_DMA2D_WAIT,
|
SYSCALL_DMA2D_WAIT,
|
||||||
SYSCALL_DMA2D_RGB565_FILL,
|
SYSCALL_DMA2D_RGB565_FILL,
|
||||||
|
@ -733,6 +733,11 @@ bool jpegdec_get_slice_rgba8888(uint32_t *rgba8888, jpegdec_slice_t *slice) {
|
|||||||
SYSCALL_JPEGDEC_GET_SLICE_RGBA8888);
|
SYSCALL_JPEGDEC_GET_SLICE_RGBA8888);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jpegdec_get_slice_mono8(uint32_t *mono8, jpegdec_slice_t *slice) {
|
||||||
|
return (bool)syscall_invoke2((uint32_t)mono8, (uint32_t)slice,
|
||||||
|
SYSCALL_JPEGDEC_GET_SLICE_MONO8);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // USE_HW_JPEG_DECODER
|
#endif // USE_HW_JPEG_DECODER
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
@ -878,6 +878,22 @@ access_violation:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jpegdec_get_slice_mono8__verified(void *mono8, jpegdec_slice_t *slice) {
|
||||||
|
if (!probe_write_access(mono8, JPEGDEC_RGBA8888_BUFFER_SIZE)) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!probe_write_access(slice, sizeof(*slice))) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jpegdec_get_slice_mono8(mono8, slice);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // USE_HW_JPEG_DECODER
|
#endif // USE_HW_JPEG_DECODER
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
@ -230,6 +230,8 @@ bool jpegdec_get_info__verified(jpegdec_image_t *image);
|
|||||||
bool jpegdec_get_slice_rgba8888__verified(void *rgba8888,
|
bool jpegdec_get_slice_rgba8888__verified(void *rgba8888,
|
||||||
jpegdec_slice_t *slice);
|
jpegdec_slice_t *slice);
|
||||||
|
|
||||||
|
bool jpegdec_get_slice_mono8__verified(void *mono8, jpegdec_slice_t *slice);
|
||||||
|
|
||||||
#endif // USE_HW_JPEG_DECODER
|
#endif // USE_HW_JPEG_DECODER
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user