This repository has been archived on 2023-07-17. You can view files and clone it, but cannot push or open issues or pull requests.
bl_mcu_sdk/components/lvgl/extra/libs/sjpg/lv_sjpg.c

904 lines
32 KiB
C
Raw Normal View History

/**
* @file lv_sjpg.c
*
*/
/*----------------------------------------------------------------------------------------------------------------------------------
/ Added normal JPG support [7/10/2020]
/ ----------
/ SJPEG is a custom created modified JPEG file format for small embedded platforms.
/ It will contain multiple JPEG fragments all embedded into a single file with a custom header.
/ This makes JPEG decoding easier using any JPEG library. Overall file size will be almost
/ similar to the parent jpeg file. We can generate sjpeg from any jpeg using a python script
/ provided along with this project.
/ (by vinodstanur | 2020 )
/ SJPEG FILE STRUCTURE
/ --------------------------------------------------------------------------------------------------------------------------------
/ Bytes | Value |
/ --------------------------------------------------------------------------------------------------------------------------------
/
/ 0 - 7 | "_SJPG__" followed by '\0'
/
/ 8 - 13 | "V1.00" followed by '\0' [VERSION OF SJPG FILE for future compatibiliby]
/
/ 14 - 15 | X_RESOLUTION (width) [little endian]
/
/ 16 - 17 | Y_RESOLUTION (height) [little endian]
/
/ 18 - 19 | TOTAL_FRAMES inside sjpeg [little endian]
/
/ 20 - 21 | JPEG BLOCK WIDTH (16 normally) [little endian]
/
/ 22 - [(TOTAL_FRAMES*2 )] | SIZE OF EACH JPEG SPLIT FRAGMENTS (FRAME_INFO_ARRAY)
/
/ SJPEG data | Each JPEG frame can be extracted from SJPEG data by parsing the FRAME_INFO_ARRAY one time.
/
/----------------------------------------------------------------------------------------------------------------------------------
/ JPEG DECODER
/ ------------
/ We are using TJpgDec - Tiny JPEG Decompressor library from ELM-CHAN for decoding each split-jpeg fragments.
/ The tjpgd.c and tjpgd.h is not modified and those are used as it is. So if any update comes for the tiny-jpeg,
/ just replace those files with updated files.
/---------------------------------------------------------------------------------------------------------------------------------*/
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
#if LV_USE_SJPG
#include "tjpgd.h"
#include "lv_sjpg.h"
#include "../../../misc/lv_fs.h"
/*********************
* DEFINES
*********************/
#define TJPGD_WORKBUFF_SIZE 4096 //Recommended by TJPGD libray
//NEVER EDIT THESE OFFSET VALUES
#define SJPEG_VERSION_OFFSET 8
#define SJPEG_X_RES_OFFSET 14
#define SJPEG_y_RES_OFFSET 16
#define SJPEG_TOTAL_FRAMES_OFFSET 18
#define SJPEG_BLOCK_WIDTH_OFFSET 20
#define SJPEG_FRAME_INFO_ARRAY_OFFSET 22
/**********************
* TYPEDEFS
**********************/
enum io_source_type {
SJPEG_IO_SOURCE_C_ARRAY,
SJPEG_IO_SOURCE_DISK,
};
typedef struct {
enum io_source_type type;
lv_fs_file_t lv_file;
uint8_t *img_cache_buff;
int img_cache_x_res;
int img_cache_y_res;
uint8_t *raw_sjpg_data; //Used when type==SJPEG_IO_SOURCE_C_ARRAY.
uint32_t raw_sjpg_data_size; //Num bytes pointed to by raw_sjpg_data.
uint32_t raw_sjpg_data_next_read_pos; //Used for all types.
} io_source_t;
typedef struct {
uint8_t *sjpeg_data;
uint32_t sjpeg_data_size;
int sjpeg_x_res;
int sjpeg_y_res;
int sjpeg_total_frames;
int sjpeg_single_frame_height;
int sjpeg_cache_frame_index;
uint8_t **frame_base_array; //to save base address of each split frames upto sjpeg_total_frames.
int *frame_base_offset; //to save base offset for fseek
uint8_t *frame_cache;
uint8_t *workb; //JPG work buffer for jpeg library
JDEC *tjpeg_jd;
io_source_t io;
} SJPEG;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t decoder_info(lv_img_decoder_t *decoder, const void *src, lv_img_header_t *header);
static lv_res_t decoder_open(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc);
static lv_res_t decoder_read_line(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t *buf);
static void decoder_close(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc);
static size_t input_func(JDEC *jd, uint8_t *buff, size_t ndata);
static int is_jpg(const uint8_t *raw_data, size_t len);
static void lv_sjpg_cleanup(SJPEG *sjpeg);
static void lv_sjpg_free(SJPEG *sjpeg);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_split_jpeg_init(void)
{
lv_img_decoder_t *dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_close_cb(dec, decoder_close);
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about an SJPG / JPG image
* @param decoder pointer to the decoder where this function belongs
* @param src can be file name or pointer to a C array
* @param header store the info here
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_info(lv_img_decoder_t *decoder, const void *src, lv_img_header_t *header)
{
LV_UNUSED(decoder);
/*Check whether the type `src` is known by the decoder*/
/* Read the SJPG/JPG header and find `width` and `height` */
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
lv_res_t ret = LV_RES_OK;
if (src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t *img_dsc = src;
uint8_t *raw_sjpeg_data = (uint8_t *)img_dsc->data;
const uint32_t raw_sjpeg_data_size = img_dsc->data_size;
if (!strncmp((char *)raw_sjpeg_data, "_SJPG__", strlen("_SJPG__"))) {
raw_sjpeg_data += 14; //seek to res info ... refer sjpeg format
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
header->w = *raw_sjpeg_data++;
header->w |= *raw_sjpeg_data++ << 8;
header->h = *raw_sjpeg_data++;
header->h |= *raw_sjpeg_data++ << 8;
return ret;
} else if (is_jpg(raw_sjpeg_data, raw_sjpeg_data_size) == true) {
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
uint8_t *workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!workb_temp)
return LV_RES_INV;
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
io_source_temp.raw_sjpg_data = raw_sjpeg_data;
io_source_temp.raw_sjpg_data_size = raw_sjpeg_data_size;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
JDEC jd_tmp;
JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
if (rc == JDR_OK) {
header->w = jd_tmp.width;
header->h = jd_tmp.height;
} else {
ret = LV_RES_INV;
goto end;
}
end:
lv_mem_free(workb_temp);
return ret;
}
} else if (src_type == LV_IMG_SRC_FILE) {
const char *fn = src;
if (strcmp(lv_fs_get_ext(fn), "sjpg") == 0) {
uint8_t buff[22];
memset(buff, 0, sizeof(buff));
lv_fs_file_t file;
lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD);
if (res != LV_FS_RES_OK)
return 78;
uint32_t rn;
res = lv_fs_read(&file, buff, 8, &rn);
if (res != LV_FS_RES_OK || rn != 8) {
lv_fs_close(&file);
return LV_RES_INV;
}
if (strcmp((char *)buff, "_SJPG__") == 0) {
lv_fs_seek(&file, 14, LV_FS_SEEK_SET);
res = lv_fs_read(&file, buff, 4, &rn);
if (res != LV_FS_RES_OK || rn != 4) {
lv_fs_close(&file);
return LV_RES_INV;
}
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
uint8_t *raw_sjpeg_data = buff;
header->w = *raw_sjpeg_data++;
header->w |= *raw_sjpeg_data++ << 8;
header->h = *raw_sjpeg_data++;
header->h |= *raw_sjpeg_data++ << 8;
lv_fs_close(&file);
return LV_RES_OK;
}
} else if (strcmp(lv_fs_get_ext(fn), "jpg") == 0) {
lv_fs_file_t file;
lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD);
if (res != LV_FS_RES_OK)
return 78;
uint8_t *workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!workb_temp) {
lv_fs_close(&file);
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_DISK;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
io_source_temp.img_cache_buff = NULL;
io_source_temp.lv_file = file;
JDEC jd_tmp;
JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
lv_fs_close(&file);
if (rc == JDR_OK) {
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
header->w = jd_tmp.width;
header->h = jd_tmp.height;
return LV_RES_OK;
}
}
}
return LV_RES_INV;
}
static int img_data_cb(JDEC *jd, void *data, JRECT *rect)
{
io_source_t *io = jd->device;
uint8_t *cache = io->img_cache_buff;
const int xres = io->img_cache_x_res;
uint8_t *buf = data;
const int INPUT_PIXEL_SIZE = 3;
const int row_width = rect->right - rect->left + 1; // Row width in pixels.
const int row_size = row_width * INPUT_PIXEL_SIZE; // Row size (bytes).
for (int y = rect->top; y <= rect->bottom; y++) {
int row_offset = y * xres * INPUT_PIXEL_SIZE + rect->left * INPUT_PIXEL_SIZE;
memcpy(cache + row_offset, buf, row_size);
buf += row_size;
}
return 1;
}
static size_t input_func(JDEC *jd, uint8_t *buff, size_t ndata)
{
io_source_t *io = jd->device;
if (!io)
return 0;
if (io->type == SJPEG_IO_SOURCE_C_ARRAY) {
const uint32_t bytes_left = io->raw_sjpg_data_size - io->raw_sjpg_data_next_read_pos;
const uint32_t to_read = ndata <= bytes_left ? (uint32_t)ndata : bytes_left;
if (to_read == 0)
return 0;
if (buff) {
memcpy(buff, io->raw_sjpg_data + io->raw_sjpg_data_next_read_pos, to_read);
}
io->raw_sjpg_data_next_read_pos += to_read;
return to_read;
} else if (io->type == SJPEG_IO_SOURCE_DISK) {
lv_fs_file_t *lv_file_p = &(io->lv_file);
if (buff) {
uint32_t rn = 0;
lv_fs_read(lv_file_p, buff, (uint32_t)ndata, &rn);
return rn;
} else {
uint32_t pos;
lv_fs_tell(lv_file_p, &pos);
lv_fs_seek(lv_file_p, (uint32_t)(ndata + pos), LV_FS_SEEK_SET);
return ndata;
}
}
return 0;
}
/**
* Open SJPG image and return the decided image
* @param decoder pointer to the decoder where this function belongs
* @param dsc pointer to a descriptor which describes this decoding session
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_open(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc)
{
LV_UNUSED(decoder);
lv_res_t lv_ret = LV_RES_OK;
if (dsc->src_type == LV_IMG_SRC_VARIABLE) {
uint8_t *data;
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)dsc->src)->data_size;
if (sjpeg == NULL) {
sjpeg = lv_mem_alloc(sizeof(SJPEG));
if (!sjpeg)
return LV_RES_INV;
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
}
if (!strncmp((char *)sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__"))) {
data = sjpeg->sjpeg_data;
data += 14;
sjpeg->sjpeg_x_res = *data++;
sjpeg->sjpeg_x_res |= *data++ << 8;
sjpeg->sjpeg_y_res = *data++;
sjpeg->sjpeg_y_res |= *data++ << 8;
sjpeg->sjpeg_total_frames = *data++;
sjpeg->sjpeg_total_frames |= *data++ << 8;
sjpeg->sjpeg_single_frame_height = *data++;
sjpeg->sjpeg_single_frame_height |= *data++ << 8;
sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
if (!sjpeg->frame_base_array) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->frame_base_offset = NULL;
uint8_t *img_frame_base = data + sjpeg->sjpeg_total_frames * 2;
sjpeg->frame_base_array[0] = img_frame_base;
for (int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
int offset = *data++;
offset |= *data++ << 8;
sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i - 1] + offset;
}
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 /*2*/);
if (!sjpeg->frame_cache) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!sjpeg->workb) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
if (!sjpeg->tjpeg_jd) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
sjpeg->io.lv_file.file_d = NULL;
dsc->img_data = NULL;
return lv_ret;
} else if (is_jpg(sjpeg->sjpeg_data, raw_sjpeg_data_size) == true) {
uint8_t *workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!workb_temp) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
io_source_temp.raw_sjpg_data = sjpeg->sjpeg_data;
io_source_temp.raw_sjpg_data_size = sjpeg->sjpeg_data_size;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
JDEC jd_tmp;
JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
if (rc == JDR_OK) {
sjpeg->sjpeg_x_res = jd_tmp.width;
sjpeg->sjpeg_y_res = jd_tmp.height;
sjpeg->sjpeg_total_frames = 1;
sjpeg->sjpeg_single_frame_height = jd_tmp.height;
sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
if (!sjpeg->frame_base_array) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->frame_base_offset = NULL;
uint8_t *img_frame_base = sjpeg->sjpeg_data;
sjpeg->frame_base_array[0] = img_frame_base;
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
if (!sjpeg->frame_cache) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!sjpeg->workb) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
if (!sjpeg->tjpeg_jd) {
lv_sjpg_cleanup(sjpeg);
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
sjpeg->io.lv_file.file_d = NULL;
dsc->img_data = NULL;
return lv_ret;
} else {
lv_ret = LV_RES_INV;
goto end;
}
end:
lv_mem_free(workb_temp);
return lv_ret;
}
} else if (dsc->src_type == LV_IMG_SRC_FILE) {
/* If all fine, then the file will be kept open */
const char *fn = dsc->src;
uint8_t *data;
if (strcmp(lv_fs_get_ext(fn), "sjpg") == 0) {
uint8_t buff[22];
memset(buff, 0, sizeof(buff));
lv_fs_file_t lv_file;
lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
if (res != LV_FS_RES_OK) {
return 78;
}
uint32_t rn;
res = lv_fs_read(&lv_file, buff, 22, &rn);
if (res != LV_FS_RES_OK || rn != 22) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
if (strcmp((char *)buff, "_SJPG__") == 0) {
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
if (sjpeg == NULL) {
sjpeg = lv_mem_alloc(sizeof(SJPEG));
if (!sjpeg) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
}
data = buff;
data += 14;
sjpeg->sjpeg_x_res = *data++;
sjpeg->sjpeg_x_res |= *data++ << 8;
sjpeg->sjpeg_y_res = *data++;
sjpeg->sjpeg_y_res |= *data++ << 8;
sjpeg->sjpeg_total_frames = *data++;
sjpeg->sjpeg_total_frames |= *data++ << 8;
sjpeg->sjpeg_single_frame_height = *data++;
sjpeg->sjpeg_single_frame_height |= *data++ << 8;
sjpeg->frame_base_array = NULL; //lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
sjpeg->frame_base_offset = lv_mem_alloc(sizeof(int) * sjpeg->sjpeg_total_frames);
if (!sjpeg->frame_base_offset) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames * 2);
sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now...
for (int i = 1; i < sjpeg->sjpeg_total_frames; i++) {
res = lv_fs_read(&lv_file, buff, 2, &rn);
if (res != LV_FS_RES_OK || rn != 2) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
data = buff;
int offset = *data++;
offset |= *data++ << 8;
sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i - 1] + offset;
}
sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time.
sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
if (!sjpeg->frame_cache) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!sjpeg->workb) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
if (!sjpeg->tjpeg_jd) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
sjpeg->io.lv_file = lv_file;
dsc->img_data = NULL;
return LV_RES_OK;
}
} else if (strcmp(lv_fs_get_ext(fn), "jpg") == 0) {
lv_fs_file_t lv_file;
lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD);
if (res != LV_FS_RES_OK) {
return LV_RES_INV;
}
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
if (sjpeg == NULL) {
sjpeg = lv_mem_alloc(sizeof(SJPEG));
if (!sjpeg) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data;
sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size;
}
uint8_t *workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!workb_temp) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_DISK;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
io_source_temp.img_cache_buff = NULL;
io_source_temp.lv_file = lv_file;
JDEC jd_tmp;
JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
if (rc == JDR_OK) {
sjpeg->sjpeg_x_res = jd_tmp.width;
sjpeg->sjpeg_y_res = jd_tmp.height;
sjpeg->sjpeg_total_frames = 1;
sjpeg->sjpeg_single_frame_height = jd_tmp.height;
sjpeg->frame_base_array = NULL;
sjpeg->frame_base_offset = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames);
if (!sjpeg->frame_base_offset) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
int img_frame_start_offset = 0;
sjpeg->frame_base_offset[0] = img_frame_start_offset;
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3);
if (!sjpeg->frame_cache) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE);
if (!sjpeg->workb) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC));
if (!sjpeg->tjpeg_jd) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
sjpeg->io.lv_file = lv_file;
dsc->img_data = NULL;
return LV_RES_OK;
} else {
if (dsc->user_data)
lv_mem_free(dsc->user_data);
lv_fs_close(&lv_file);
return LV_RES_INV;
}
}
}
return LV_RES_INV;
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL)
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
static lv_res_t decoder_read_line(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc, lv_coord_t x, lv_coord_t y,
lv_coord_t len, uint8_t *buf)
{
LV_UNUSED(decoder);
if (dsc->src_type == LV_IMG_SRC_VARIABLE) {
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
JRESULT rc;
int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
/*If line not from cache, refresh cache */
if (sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[sjpeg_req_frame_index];
if (sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) {
/*This is the last frame. */
const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data);
sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset;
} else {
sjpeg->io.raw_sjpg_data_size =
(uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data);
}
sjpeg->io.raw_sjpg_data_next_read_pos = 0;
rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
if (rc != JDR_OK)
return LV_RES_INV;
rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
if (rc != JDR_OK)
return LV_RES_INV;
sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
}
int offset = 0;
uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res * 3;
#if LV_COLOR_DEPTH == 32
for (int i = 0; i < len; i++) {
buf[offset + 3] = 0xff;
buf[offset + 2] = *cache++;
buf[offset + 1] = *cache++;
buf[offset + 0] = *cache++;
offset += 4;
}
#elif LV_COLOR_DEPTH == 16
for (int i = 0; i < len; i++) {
uint16_t col_16bit = (*cache++ & 0xf8) << 8;
col_16bit |= (*cache++ & 0xFC) << 3;
col_16bit |= (*cache++ >> 3);
#if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1
buf[offset++] = col_16bit >> 8;
buf[offset++] = col_16bit & 0xff;
#else
buf[offset++] = col_16bit & 0xff;
buf[offset++] = col_16bit >> 8;
#endif // LV_BIG_ENDIAN_SYSTEM
}
#elif LV_COLOR_DEPTH == 8
for (int i = 0; i < len; i++) {
uint8_t col_8bit = (*cache++ & 0xC0);
col_8bit |= (*cache++ & 0xe0) >> 2;
col_8bit |= (*cache++ & 0xe0) >> 5;
buf[offset++] = col_8bit;
}
#else
#error Unsupported LV_COLOR_DEPTH
#endif // LV_COLOR_DEPTH
return LV_RES_OK;
} else if (dsc->src_type == LV_IMG_SRC_FILE) {
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
JRESULT rc;
int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
lv_fs_file_t *lv_file_p = &(sjpeg->io.lv_file);
if (!lv_file_p)
goto end;
/*If line not from cache, refresh cache */
if (sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset[sjpeg_req_frame_index]);
lv_fs_seek(&(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET);
rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
if (rc != JDR_OK)
return LV_RES_INV;
rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0);
if (rc != JDR_OK)
return LV_RES_INV;
sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
}
int offset = 0;
uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res * 3;
#if LV_COLOR_DEPTH == 32
for (int i = 0; i < len; i++) {
buf[offset + 3] = 0xff;
buf[offset + 2] = *cache++;
buf[offset + 1] = *cache++;
buf[offset + 0] = *cache++;
offset += 4;
}
#elif LV_COLOR_DEPTH == 16
for (int i = 0; i < len; i++) {
uint16_t col_8bit = (*cache++ & 0xf8) << 8;
col_8bit |= (*cache++ & 0xFC) << 3;
col_8bit |= (*cache++ >> 3);
#if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1
buf[offset++] = col_8bit >> 8;
buf[offset++] = col_8bit & 0xff;
#else
buf[offset++] = col_8bit & 0xff;
buf[offset++] = col_8bit >> 8;
#endif // LV_BIG_ENDIAN_SYSTEM
}
#elif LV_COLOR_DEPTH == 8
for (int i = 0; i < len; i++) {
uint8_t col_8bit = (*cache++ & 0xC0);
col_8bit |= (*cache++ & 0xe0) >> 2;
col_8bit |= (*cache++ & 0xe0) >> 5;
buf[offset++] = col_8bit;
}
#else
#error Unsupported LV_COLOR_DEPTH
#endif // LV_COLOR_DEPTH
return LV_RES_OK;
}
end:
return LV_RES_INV;
}
/**
* Free the allocated resources
* @param decoder pointer to the decoder where this function belongs
* @param dsc pointer to a descriptor which describes this decoding session
*/
static void decoder_close(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc)
{
LV_UNUSED(decoder);
/*Free all allocated data*/
SJPEG *sjpeg = (SJPEG *)dsc->user_data;
if (!sjpeg)
return;
switch (dsc->src_type) {
case LV_IMG_SRC_FILE:
if (sjpeg->io.lv_file.file_d) {
lv_fs_close(&(sjpeg->io.lv_file));
}
lv_sjpg_cleanup(sjpeg);
break;
case LV_IMG_SRC_VARIABLE:
lv_sjpg_cleanup(sjpeg);
break;
default:;
}
}
static int is_jpg(const uint8_t *raw_data, size_t len)
{
const uint8_t jpg_signature[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46 };
if (len < sizeof(jpg_signature))
return false;
return memcmp(jpg_signature, raw_data, sizeof(jpg_signature)) == 0;
}
static void lv_sjpg_free(SJPEG *sjpeg)
{
if (sjpeg->frame_cache)
lv_mem_free(sjpeg->frame_cache);
if (sjpeg->frame_base_array)
lv_mem_free(sjpeg->frame_base_array);
if (sjpeg->frame_base_offset)
lv_mem_free(sjpeg->frame_base_offset);
if (sjpeg->tjpeg_jd)
lv_mem_free(sjpeg->tjpeg_jd);
if (sjpeg->workb)
lv_mem_free(sjpeg->workb);
}
static void lv_sjpg_cleanup(SJPEG *sjpeg)
{
if (!sjpeg)
return;
lv_sjpg_free(sjpeg);
lv_mem_free(sjpeg);
}
#endif /*LV_USE_SJPG*/