mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-15 23:05:45 +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.
|
||||
#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 enum {
|
||||
@ -43,7 +46,7 @@ typedef enum {
|
||||
// (jpegdec_get_info can be called to get the image info)
|
||||
JPEGDEC_STATE_INFO_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,
|
||||
// Decoding is finished
|
||||
JPEGDEC_STATE_FINISHED,
|
||||
@ -125,3 +128,12 @@ bool jpegdec_get_info(jpegdec_image_t* info);
|
||||
// Can be called immediately after `jpegdec_process` returns
|
||||
// `JPEGDEC_STATE_SLICE_READY`.
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -416,6 +416,7 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("haptic_play_custom")
|
||||
// jpegdec
|
||||
.allowlist_var("JPEGDEC_RGBA8888_BUFFER_SIZE")
|
||||
.allowlist_var("JPEGDEC_MONO8_BUFFER_SIZE")
|
||||
.allowlist_type("jpegdec_state_t")
|
||||
.allowlist_type("jpegdec_image_t")
|
||||
.allowlist_type("jpegdec_image_format_t")
|
||||
@ -424,7 +425,8 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("jpegdec_close")
|
||||
.allowlist_function("jpegdec_process")
|
||||
.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.
|
||||
bindings
|
||||
|
@ -9,6 +9,7 @@ use crate::io::BinaryData;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
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)]
|
||||
enum JpegDecState {
|
||||
@ -129,6 +130,33 @@ impl<'a> JpegDecoder<'a> {
|
||||
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 {
|
||||
if self.buff_pos == self.buff_len {
|
||||
self.buff_len = self.jpeg.read(self.jpeg_pos, &mut self.buff);
|
||||
@ -173,8 +201,8 @@ impl<'a> JpegDecoder<'a> {
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - `rgba_u32` is a valid pointer to a mutable buffer of u32 of length at
|
||||
// least `RGBA8888_BUFFER_SIZE`
|
||||
// - `rgba_u32` is a valid pointer to a mutable buffer of length at least
|
||||
// `RGBA8888_BUFFER_SIZE` aligned to u32
|
||||
// - `slice` is a valid pointer to a mutable `jpegdec_slice_t`
|
||||
// - `jpegdec_get_slice_rgba8888` doesn't retain the pointers to the data for
|
||||
// later use
|
||||
@ -201,4 +229,52 @@ impl<'a> JpegDecoder<'a> {
|
||||
|
||||
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]);
|
||||
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
|
||||
|
||||
#ifdef USE_DMA2D
|
||||
|
@ -157,6 +157,7 @@ typedef enum {
|
||||
SYSCALL_JPEGDEC_PROCESS,
|
||||
SYSCALL_JPEGDEC_GET_INFO,
|
||||
SYSCALL_JPEGDEC_GET_SLICE_RGBA8888,
|
||||
SYSCALL_JPEGDEC_GET_SLICE_MONO8,
|
||||
|
||||
SYSCALL_DMA2D_WAIT,
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// =============================================================================
|
||||
|
@ -878,6 +878,22 @@ access_violation:
|
||||
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
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
@ -230,6 +230,8 @@ bool jpegdec_get_info__verified(jpegdec_image_t *image);
|
||||
bool jpegdec_get_slice_rgba8888__verified(void *rgba8888,
|
||||
jpegdec_slice_t *slice);
|
||||
|
||||
bool jpegdec_get_slice_mono8__verified(void *mono8, jpegdec_slice_t *slice);
|
||||
|
||||
#endif // USE_HW_JPEG_DECODER
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user