imlib: Add hardware JPEG decoder support.

This commit is contained in:
iabdalkader 2022-02-27 17:35:36 +02:00
parent b5b26ff390
commit 38c26cec92

View File

@ -13,14 +13,281 @@
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#define TIME_JPEG (1)
#define TIME_JPEG (0)
#if (TIME_JPEG == 1)
#include "py/mphal.h"
#include <stdio.h>
#endif
#include "imlib.h"
/* Defines and variables */
static bool jpeg_is_progressive(image_t *img)
{
uint8_t *data = img->data;
for (uint32_t i=0; i<img->size;) {
uint8_t marker = data[i++];
if (marker == 0xFF) {
continue;
} else if (marker == 0xD8) { //SOI
continue;
} else if (marker == 0xC0) { //SOF0/baseline
return false;
} else if (marker == 0xC2) { //SOF2/progressive
return true;
} else if (marker == 0xC2) { //EOI
return false;
} else if (marker == 0xDD) { //DRI 4 bytes payload.
i += 4;
} else if (marker >= 0xd0 && marker <= 0xd7) { //RSTn
continue;
} else if (i+2 < img->size) { // Other markers, variable size payload.
i += __REV16(*((uint16_t*)(&data[i])));
} else {
return false;
}
}
return false;
}
#if 0 //(OMV_HARDWARE_JPEG == 1)
/* Hardware JPEG decoder */
#include STM32_HAL_H
#define JPEG_MCU_SIZE_444 (192)
#define JPEG_MCU_SIZE_422 (256)
#define JPEG_MCU_SIZE_420 (384)
#define JPEG_MCU_SIZE_GRAY (64)
#define debug_printf(...) //printf(__VA_ARGS__)
typedef struct {
uint32_t width;
uint32_t height;
uint8_t *pixels;
uint32_t bpp;
uint32_t error;
pixformat_t pixfmt;
uint32_t mcu_count;
uint32_t mcu_width;
uint32_t mcu_height;
uint32_t mcu_per_line;
uint8_t *mcu_buffer[2];
uint32_t mcu_buffer_size;
} jpeg_state_t;
static jpeg_state_t *jpeg_state = NULL;
static JPEG_HandleTypeDef hjpeg = {0};
static DMA2D_HandleTypeDef hdma2d = {0};
static int dma2d_config(uint32_t pixfmt, uint32_t colorspace, uint32_t subsampling)
{
// Initial DMA2D configuration.
hdma2d.Instance = DMA2D;
HAL_DMA2D_DeInit(&hdma2d);
hdma2d.XferCpltCallback = NULL;
hdma2d.XferErrorCallback = NULL;
// Configure DMA2D output.
hdma2d.Init.OutputOffset = 0;
hdma2d.Init.ColorMode = DMA2D_OUTPUT_RGB565; // Ignored in M2M mode.
hdma2d.Init.RedBlueSwap = DMA2D_RB_REGULAR;
hdma2d.Init.AlphaInverted = DMA2D_REGULAR_ALPHA;
hdma2d.Init.BytesSwap = DMA2D_BYTES_REGULAR;
hdma2d.Init.LineOffsetMode = DMA2D_LOM_PIXELS;
// Configure DMA2D input.
hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
hdma2d.LayerCfg[1].InputAlpha = 0xFF;
hdma2d.LayerCfg[1].InputOffset = 0;
hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR;
hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;
hdma2d.LayerCfg[1].ChromaSubSampling = subsampling;
if (pixfmt == PIXFORMAT_GRAYSCALE && colorspace == JPEG_GRAYSCALE_COLORSPACE) {
hdma2d.Init.Mode = DMA2D_M2M; // GS -> GS, no PFC.
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_L8;
} else if (pixfmt == PIXFORMAT_RGB565 && colorspace == JPEG_YCBCR_COLORSPACE) {
hdma2d.Init.Mode = DMA2D_M2M_PFC;
hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_YCBCR;
} else { // Unsupported pixel format conversion
return -1;
}
// DMA2D initialization.
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
return 0;
}
static void dma2d_copy_mcu(uint32_t x, uint32_t y, uint32_t mcu_width, uint32_t mcu_height, uint8_t *mcu_buffer)
{
// Wait for previous transfer.
if (HAL_DMA2D_PollForTransfer(&hdma2d, 1000) != HAL_OK) {
// TODO abort transfer and decoding, and set error flag.
}
// Configure DMA2D output.
hdma2d.Init.OutputOffset = jpeg_state->width - mcu_width;
hdma2d.LayerCfg[1].InputOffset = jpeg_state->mcu_width - mcu_width;
// DMA2D initialization.
HAL_DMA2D_Init(&hdma2d);
HAL_DMA2D_ConfigLayer(&hdma2d, 1);
uint8_t *dst = jpeg_state->pixels + (y * jpeg_state->width + x) * jpeg_state->bpp;
// Output size in pixels per line and number of lines
HAL_DMA2D_Start(&hdma2d, (uint32_t) mcu_buffer, (uint32_t) dst, mcu_width, mcu_height);
}
static void jpeg_info_ready_callback(JPEG_HandleTypeDef *hjpeg, JPEG_ConfTypeDef *jpeg_info)
{
uint32_t subsampling = DMA2D_NO_CSS;
debug_printf("colorspace %ld subsampling %ld width %ld height %ld quality %ld\n",
jpeg_info->ColorSpace, jpeg_info->ChromaSubsampling,
jpeg_info->ImageWidth, jpeg_info->ImageHeight, jpeg_info->ImageQuality);
switch (jpeg_info->ChromaSubsampling) {
case JPEG_420_SUBSAMPLING:
subsampling = DMA2D_CSS_420;
jpeg_state->mcu_width = 16;
jpeg_state->mcu_height = 16;
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_420;
break;
case JPEG_422_SUBSAMPLING:
subsampling = DMA2D_CSS_422;
jpeg_state->mcu_width = 16;
jpeg_state->mcu_height = 8;
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_422;
break;
case JPEG_444_SUBSAMPLING:
subsampling = DMA2D_NO_CSS;
jpeg_state->mcu_width = 8;
jpeg_state->mcu_height = 8;
if (jpeg_info->ColorSpace == JPEG_YCBCR_COLORSPACE) {
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_444;
} else { // Grayscale
jpeg_state->mcu_buffer_size = JPEG_MCU_SIZE_GRAY;
}
break;
default: // unknown subsampling.
goto config_error;
}
if (dma2d_config(jpeg_state->pixfmt, jpeg_info->ColorSpace, subsampling) != 0) {
goto config_error;
}
// Allocate MCU buffer(s).
jpeg_state->mcu_buffer[0] = fb_alloc(jpeg_state->mcu_buffer_size, FB_ALLOC_PREFER_SPEED | FB_ALLOC_CACHE_ALIGN);
jpeg_state->mcu_buffer[1] = fb_alloc(jpeg_state->mcu_buffer_size, FB_ALLOC_PREFER_SPEED | FB_ALLOC_CACHE_ALIGN);
jpeg_state->mcu_per_line = (jpeg_state->width / jpeg_state->mcu_width) + !!(jpeg_state->width % jpeg_state->mcu_width);
// Set JPEG output buffer.
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[0], jpeg_state->mcu_buffer_size);
return;
config_error:
HAL_JPEG_Abort(hjpeg);
jpeg_state->error = true;
return;
}
static void jpeg_data_ready_callback(JPEG_HandleTypeDef *hjpeg, uint8_t *mcu_buffer, uint32_t length)
{
uint32_t mcu_width = jpeg_state->mcu_width;
uint32_t mcu_height = jpeg_state->mcu_height;
uint32_t x = (jpeg_state->mcu_count % jpeg_state->mcu_per_line) * mcu_width;
uint32_t y = (jpeg_state->mcu_count / jpeg_state->mcu_per_line) * mcu_height;
if ((x + mcu_width) > jpeg_state->width) {
mcu_width = jpeg_state->width - x;
debug_printf("mcu: %lu mcu_perline %lu x:%lu y:%lu mcu_w:%lu mcu_cropped_w:%lu\n",
jpeg_state->mcu_count % jpeg_state->mcu_per_line, jpeg_state->mcu_per_line,
x, y, jpeg_state->mcu_width, mcu_width);
}
if ((y + mcu_height) > jpeg_state->height) {
mcu_height = jpeg_state->height - y;
debug_printf("mcu: %ld mcu_perline %lu x:%lu y:%lu mcu_h:%lu mcu_cropped_h:%lu\n",
jpeg_state->mcu_count % jpeg_state->mcu_per_line, jpeg_state->mcu_per_line,
x, y, jpeg_state->mcu_height, mcu_height);
}
// Flush MCU buffer cache for DMA2D.
SCB_CleanDCache_by_Addr((uint32_t *) mcu_buffer, jpeg_state->mcu_buffer_size);
// Copy MCU to target frame buffer.
dma2d_copy_mcu(x, y, mcu_width, mcu_height, mcu_buffer);
if (mcu_buffer == jpeg_state->mcu_buffer[0]) {
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[1], jpeg_state->mcu_buffer_size);
} else {
HAL_JPEG_ConfigOutputBuffer(hjpeg, jpeg_state->mcu_buffer[0], jpeg_state->mcu_buffer_size);
}
jpeg_state->mcu_count++;
}
void jpeg_decompress(image_t *dst, image_t *src)
{
hjpeg.Instance = JPEG;
HAL_JPEG_DeInit(&hjpeg);
HAL_JPEG_Init(&hjpeg);
// Register JPEG callbacks.
HAL_JPEG_RegisterDataReadyCallback(&hjpeg, jpeg_data_ready_callback);
HAL_JPEG_RegisterInfoReadyCallback(&hjpeg, jpeg_info_ready_callback);
// Supports decoding to Grayscale or RGB565 only.
if (dst->pixfmt != PIXFORMAT_GRAYSCALE && dst->pixfmt != PIXFORMAT_RGB565) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Unsupported format."));
}
// Supports decoding baseline JPEGs only.
if (jpeg_is_progressive(src)) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Progressive JPEG is not supported."));
}
// Set/reset JPEG state.
jpeg_state_t jpeg_state_stk = {
.width = dst->w,
.height = dst->h,
.pixels = dst->data,
.bpp = dst->bpp,
.error = false,
.pixfmt = dst->pixfmt,
.mcu_count = 0,
.mcu_width = 0,
.mcu_height = 0,
.mcu_per_line = 0,
.mcu_buffer = {NULL, NULL},
.mcu_buffer_size = 0,
};
jpeg_state = &jpeg_state_stk;
#if (TIME_JPEG == 1)
mp_uint_t start = mp_hal_ticks_ms();
#endif
uint8_t buf[1024];
// Start decoding JPEG headers.
if (HAL_JPEG_Decode(&hjpeg, src->data, src->size, buf, sizeof(buf), 3000) != HAL_OK || jpeg_state->error) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed"));
}
if (jpeg_state->mcu_buffer[0] != NULL) {
// Free MCU buffer(s).
fb_free();
fb_free();
}
// Invalidate the dest image cache.
SCB_InvalidateDCache_by_Addr(dst->pixels, image_size(dst));
#if (TIME_JPEG == 1)
printf("time: %u ms\n", mp_hal_ticks_ms() - start);
#endif
}
#else
/* Software JPEG decoder */
#define FILE_HIGHWATER 1536
#define JPEG_FILE_BUF_SIZE 2048
#define HUFF_TABLEN 273
@ -2815,6 +3082,11 @@ void jpeg_decompress(image_t *dst, image_t *src)
mp_uint_t start = mp_hal_ticks_ms();
#endif
// Supports decoding baseline JPEGs only.
if (jpeg_is_progressive(src)) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Progressive JPEG is not supported."));
}
if (JPEG_openRAM(&jpg, src->data, src->size, dst->data) == 0) {
// failed to parse the header
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed."));
@ -2852,3 +3124,4 @@ void jpeg_decompress(image_t *dst, image_t *src)
printf("time: %u ms\n", mp_hal_ticks_ms() - start);
#endif
}
#endif