refactor(image decoder): move lvgl binary image decoder to standalone file (#4829)
Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
@@ -276,8 +276,8 @@ open/close the PNG files. It should look like this:
|
|||||||
/*Change the color format if required. For PNG usually 'Raw' is fine*/
|
/*Change the color format if required. For PNG usually 'Raw' is fine*/
|
||||||
dsc->header.cf = LV_COLOR_FORMAT_...
|
dsc->header.cf = LV_COLOR_FORMAT_...
|
||||||
|
|
||||||
/*Call a built in decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/
|
/*Call a binary image decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/
|
||||||
lv_result_t res = lv_image_decoder_built_in_open(decoder, dsc);
|
lv_result_t res = lv_bin_decoder_open(decoder, dsc);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -306,7 +306,7 @@ open/close the PNG files. It should look like this:
|
|||||||
/*Free all allocated data*/
|
/*Free all allocated data*/
|
||||||
|
|
||||||
/*Call the built-in close function if the built-in open/read_line was used*/
|
/*Call the built-in close function if the built-in open/read_line was used*/
|
||||||
lv_image_decoder_built_in_close(decoder, dsc);
|
lv_bin_decoder_close(decoder, dsc);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
lvgl.h
1
lvgl.h
@@ -90,6 +90,7 @@ extern "C" {
|
|||||||
#include "src/others/file_explorer/lv_file_explorer.h"
|
#include "src/others/file_explorer/lv_file_explorer.h"
|
||||||
|
|
||||||
#include "src/libs/barcode/lv_barcode.h"
|
#include "src/libs/barcode/lv_barcode.h"
|
||||||
|
#include "src/libs/bin_decoder/lv_bin_decoder.h"
|
||||||
#include "src/libs/bmp/lv_bmp.h"
|
#include "src/libs/bmp/lv_bmp.h"
|
||||||
#include "src/libs/rle/lv_rle_decoder.h"
|
#include "src/libs/rle/lv_rle_decoder.h"
|
||||||
#include "src/libs/fsdrv/lv_fsdrv.h"
|
#include "src/libs/fsdrv/lv_fsdrv.h"
|
||||||
|
|||||||
@@ -22,29 +22,11 @@
|
|||||||
* TYPEDEFS
|
* TYPEDEFS
|
||||||
**********************/
|
**********************/
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
lv_fs_file_t * f;
|
|
||||||
lv_color32_t * palette;
|
|
||||||
uint8_t * img_data;
|
|
||||||
lv_opa_t * opa;
|
|
||||||
} lv_image_decoder_built_in_data_t;
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* STATIC PROTOTYPES
|
* STATIC PROTOTYPES
|
||||||
**********************/
|
**********************/
|
||||||
static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc);
|
|
||||||
static void free_decoder_data(lv_image_decoder_dsc_t * dsc);
|
|
||||||
static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
||||||
#if LV_BIN_DECODER_RAM_LOAD
|
|
||||||
static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
||||||
#endif
|
|
||||||
static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
||||||
static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x,
|
|
||||||
int32_t y,
|
|
||||||
int32_t w_px, const uint8_t * in, lv_color32_t * out);
|
|
||||||
|
|
||||||
static uint32_t img_width_to_stride(lv_image_header_t * header);
|
static uint32_t img_width_to_stride(lv_image_header_t * header);
|
||||||
static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br);
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* STATIC VARIABLES
|
* STATIC VARIABLES
|
||||||
@@ -64,21 +46,6 @@ static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buf
|
|||||||
void _lv_image_decoder_init(void)
|
void _lv_image_decoder_init(void)
|
||||||
{
|
{
|
||||||
_lv_ll_init(img_decoder_ll_p, sizeof(lv_image_decoder_t));
|
_lv_ll_init(img_decoder_ll_p, sizeof(lv_image_decoder_t));
|
||||||
|
|
||||||
lv_image_decoder_t * decoder;
|
|
||||||
|
|
||||||
/*Create a decoder for the built in color format*/
|
|
||||||
decoder = lv_image_decoder_create();
|
|
||||||
LV_ASSERT_MALLOC(decoder);
|
|
||||||
if(decoder == NULL) {
|
|
||||||
LV_LOG_WARN("Out of memory");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_image_decoder_set_info_cb(decoder, lv_image_decoder_built_in_info);
|
|
||||||
lv_image_decoder_set_open_cb(decoder, lv_image_decoder_built_in_open);
|
|
||||||
lv_image_decoder_set_get_area_cb(decoder, lv_image_decoder_built_in_get_area);
|
|
||||||
lv_image_decoder_set_close_cb(decoder, lv_image_decoder_built_in_close);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,603 +265,6 @@ void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decode
|
|||||||
decoder->close_cb = close_cb;
|
decoder->close_cb = close_cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info about a built-in image
|
|
||||||
* @param decoder the decoder where this function belongs
|
|
||||||
* @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol
|
|
||||||
* @param header store the image data here
|
|
||||||
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
|
||||||
*/
|
|
||||||
lv_result_t lv_image_decoder_built_in_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder); /*Unused*/
|
|
||||||
|
|
||||||
lv_image_src_t src_type = lv_image_src_get_type(src);
|
|
||||||
if(src_type == LV_IMAGE_SRC_VARIABLE) {
|
|
||||||
header->w = ((lv_image_dsc_t *)src)->header.w;
|
|
||||||
header->h = ((lv_image_dsc_t *)src)->header.h;
|
|
||||||
header->cf = ((lv_image_dsc_t *)src)->header.cf;
|
|
||||||
header->stride = ((lv_image_dsc_t *)src)->header.stride;
|
|
||||||
}
|
|
||||||
else if(src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
/*Support only "*.bin" files*/
|
|
||||||
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RESULT_INVALID;
|
|
||||||
|
|
||||||
lv_fs_file_t f;
|
|
||||||
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
|
|
||||||
if(res == LV_FS_RES_OK) {
|
|
||||||
uint32_t rn;
|
|
||||||
res = lv_fs_read(&f, header, sizeof(lv_image_header_t), &rn);
|
|
||||||
lv_fs_close(&f);
|
|
||||||
if(res != LV_FS_RES_OK || rn != sizeof(lv_image_header_t)) {
|
|
||||||
LV_LOG_WARN("Read file header failed: %d", res);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(src_type == LV_IMAGE_SRC_SYMBOL) {
|
|
||||||
/*The size depend on the font but it is unknown here. It should be handled outside of the
|
|
||||||
*function*/
|
|
||||||
header->w = 1;
|
|
||||||
header->h = 1;
|
|
||||||
/*Symbols always have transparent parts. Important because of cover check in the draw
|
|
||||||
*function. The actual value doesn't matter because lv_draw_label will draw it*/
|
|
||||||
header->cf = LV_COLOR_FORMAT_A8;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LV_LOG_WARN("Image get info found unknown src type");
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a built in image
|
|
||||||
* @param decoder the decoder where this function belongs
|
|
||||||
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
|
|
||||||
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
|
||||||
*/
|
|
||||||
lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder);
|
|
||||||
lv_fs_res_t res = LV_RESULT_INVALID;
|
|
||||||
|
|
||||||
/*Open the file if it's a file*/
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
/*Support only "*.bin" files*/
|
|
||||||
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RESULT_INVALID;
|
|
||||||
|
|
||||||
/*If the file was open successfully save the file descriptor*/
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc);
|
|
||||||
if(decoder_data == NULL) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->user_data = decoder_data;
|
|
||||||
lv_fs_file_t * f = lv_malloc(sizeof(*f));
|
|
||||||
if(f == NULL) {
|
|
||||||
free_decoder_data(dsc);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
LV_LOG_WARN("Open file failed: %d", res);
|
|
||||||
lv_free(f);
|
|
||||||
free_decoder_data(dsc);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder_data->f = f;
|
|
||||||
|
|
||||||
lv_color_format_t cf = dsc->header.cf;
|
|
||||||
|
|
||||||
/*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/
|
|
||||||
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
|
||||||
res = decode_indexed(decoder, dsc);
|
|
||||||
}
|
|
||||||
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
|
|
||||||
res = decode_alpha_only(decoder, dsc);
|
|
||||||
}
|
|
||||||
#if LV_BIN_DECODER_RAM_LOAD
|
|
||||||
else if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888
|
|
||||||
|| cf == LV_COLOR_FORMAT_RGB888 || cf == LV_COLOR_FORMAT_RGB565) {
|
|
||||||
res = decode_rgb(decoder, dsc);
|
|
||||||
}
|
|
||||||
else if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
|
||||||
res = decode_rgb(decoder, dsc);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
else {
|
|
||||||
/* decode them in get_area_cb */
|
|
||||||
res = LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
|
||||||
/*The variables should have valid data*/
|
|
||||||
lv_image_dsc_t * img_dsc = (lv_image_dsc_t *)dsc->src;
|
|
||||||
if(img_dsc->data == NULL) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_color_format_t cf = img_dsc->header.cf;
|
|
||||||
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
|
||||||
/*Need decoder data to store converted image*/
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc);
|
|
||||||
if(decoder_data == NULL) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = decode_indexed(decoder, dsc);
|
|
||||||
}
|
|
||||||
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
|
|
||||||
/*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = get_decoder_data(dsc);
|
|
||||||
if(decoder_data == NULL) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = decode_alpha_only(decoder, dsc);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/*In case of uncompressed formats the image stored in the ROM/RAM.
|
|
||||||
*So simply give its pointer*/
|
|
||||||
dsc->img_data = ((lv_image_dsc_t *)dsc->src)->data;
|
|
||||||
res = LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(res != LV_RESULT_OK) {
|
|
||||||
free_decoder_data(dsc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the pending decoding. Free resources etc.
|
|
||||||
* @param decoder pointer to the decoder the function associated with
|
|
||||||
* @param dsc pointer to decoder descriptor
|
|
||||||
*/
|
|
||||||
void lv_image_decoder_built_in_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder); /*Unused*/
|
|
||||||
free_decoder_data(dsc);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
|
||||||
const lv_area_t * full_area, lv_area_t * decoded_area)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder); /*Unused*/
|
|
||||||
|
|
||||||
lv_color_format_t cf = dsc->header.cf;
|
|
||||||
/*Check if cf is supported*/
|
|
||||||
|
|
||||||
bool supported = LV_COLOR_FORMAT_IS_INDEXED(cf)
|
|
||||||
|| cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888
|
|
||||||
|| cf == LV_COLOR_FORMAT_RGB565 || cf == LV_COLOR_FORMAT_RGB565A8;
|
|
||||||
if(!supported) {
|
|
||||||
LV_LOG_WARN("CF: %d is not supported", cf);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_result_t res = LV_RESULT_INVALID;
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data;
|
|
||||||
lv_fs_file_t * f = decoder_data->f;
|
|
||||||
uint32_t bpp = lv_color_format_get_bpp(cf);
|
|
||||||
int32_t w_px = lv_area_get_width(full_area);
|
|
||||||
uint8_t * img_data = NULL;
|
|
||||||
uint32_t offset = sizeof(lv_image_header_t); /*All image starts with image header*/
|
|
||||||
|
|
||||||
/*We only support read line by line for now*/
|
|
||||||
if(decoded_area->y1 == LV_COORD_MIN) {
|
|
||||||
/*Indexed image is converted to ARGB888*/
|
|
||||||
uint32_t len = LV_COLOR_FORMAT_IS_INDEXED(cf) ? sizeof(lv_color32_t) * 8 : bpp;
|
|
||||||
len = (len * w_px) / 8;
|
|
||||||
img_data = lv_draw_buf_malloc(len, cf);
|
|
||||||
LV_ASSERT_NULL(img_data);
|
|
||||||
if(img_data == NULL)
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
|
|
||||||
*decoded_area = *full_area;
|
|
||||||
decoded_area->y2 = decoded_area->y1;
|
|
||||||
decoder_data->img_data = img_data; /*Free on decoder close*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
decoded_area->y1++;
|
|
||||||
decoded_area->y2++;
|
|
||||||
img_data = decoder_data->img_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(decoded_area->y1 > full_area->y2) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
|
||||||
int32_t x_fraction = decoded_area->x1 % (8 / bpp);
|
|
||||||
uint32_t len = (w_px * bpp + 7) / 8 + 1; /*10px for 1bpp may across 3bytes*/
|
|
||||||
uint8_t * buf = NULL;
|
|
||||||
offset += dsc->palette_size * 4; /*Skip palette*/
|
|
||||||
offset += decoded_area->y1 * dsc->header.stride;
|
|
||||||
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
buf = lv_malloc(len);
|
|
||||||
LV_ASSERT_NULL(buf);
|
|
||||||
if(buf == NULL)
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
|
|
||||||
res = fs_read_file_at(f, offset, buf, len, NULL);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
lv_free(buf);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const lv_image_dsc_t * image = dsc->src;
|
|
||||||
buf = (void *)(image->data + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
decode_indexed_line(cf, dsc->palette, x_fraction, 0, w_px, buf, (lv_color32_t *)img_data);
|
|
||||||
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)buf);
|
|
||||||
|
|
||||||
dsc->img_data = img_data; /*Return decoded image*/
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888
|
|
||||||
|| cf == LV_COLOR_FORMAT_RGB565) {
|
|
||||||
uint32_t len = (w_px * bpp) / 8;
|
|
||||||
offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/
|
|
||||||
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
|
||||||
res = fs_read_file_at(f, offset, img_data, len, NULL);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->img_data = img_data; /*Return decoded image*/
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
|
||||||
bpp = 16; /* RGB565 + A8 mask*/
|
|
||||||
uint32_t len = (w_px * bpp) / 8; /*map comes firstly*/
|
|
||||||
offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/
|
|
||||||
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
|
||||||
res = fs_read_file_at(f, offset, img_data, len, NULL);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Now the A8 mask*/
|
|
||||||
offset = sizeof(lv_image_header_t);
|
|
||||||
offset += dsc->header.h * dsc->header.w * bpp / 8; /*Move to A8 map*/
|
|
||||||
offset += decoded_area->y1 * dsc->header.w * 1; /*Move to y1*/
|
|
||||||
offset += decoded_area->x1 * 1; /*Move to x1*/
|
|
||||||
res = fs_read_file_at(f, offset, img_data + len, w_px * 1, NULL);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->img_data = img_data; /*Return decoded image*/
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************
|
|
||||||
* STATIC FUNCTIONS
|
|
||||||
**********************/
|
|
||||||
|
|
||||||
static lv_image_decoder_built_in_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
lv_image_decoder_built_in_data_t * data = dsc->user_data;
|
|
||||||
if(data == NULL) {
|
|
||||||
data = lv_malloc_zeroed(sizeof(lv_image_decoder_built_in_data_t));
|
|
||||||
LV_ASSERT_MALLOC(data);
|
|
||||||
if(data == NULL) {
|
|
||||||
LV_LOG_ERROR("Out of memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->user_data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_decoder_data(lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data;
|
|
||||||
if(decoder_data) {
|
|
||||||
if(decoder_data->f) {
|
|
||||||
lv_fs_close(decoder_data->f);
|
|
||||||
lv_free(decoder_data->f);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_draw_buf_free(decoder_data->img_data);
|
|
||||||
lv_free(decoder_data->palette);
|
|
||||||
lv_free(decoder_data);
|
|
||||||
dsc->user_data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder); /*Unused*/
|
|
||||||
lv_result_t res;
|
|
||||||
uint32_t rn;
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data;
|
|
||||||
lv_fs_file_t * f = decoder_data->f;
|
|
||||||
lv_color_format_t cf = dsc->header.cf;
|
|
||||||
uint32_t palette_len = sizeof(lv_color32_t) * LV_COLOR_INDEXED_PALETTE_SIZE(cf);
|
|
||||||
const lv_color32_t * palette;
|
|
||||||
const uint8_t * indexed_data = NULL;
|
|
||||||
uint32_t stride = dsc->header.stride;
|
|
||||||
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
/*read palette for indexed image*/
|
|
||||||
palette = lv_malloc(palette_len);
|
|
||||||
LV_ASSERT_MALLOC(palette);
|
|
||||||
if(palette == NULL) {
|
|
||||||
LV_LOG_ERROR("Out of memory");
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = fs_read_file_at(f, sizeof(lv_image_header_t), (uint8_t *)palette, palette_len, &rn);
|
|
||||||
if(res != LV_FS_RES_OK || rn != palette_len) {
|
|
||||||
LV_LOG_WARN("Read palette failed: %d", res);
|
|
||||||
lv_free((void *)palette);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LV_BIN_DECODER_RAM_LOAD
|
|
||||||
indexed_data = lv_draw_buf_malloc(stride * dsc->header.h, cf);
|
|
||||||
LV_ASSERT_MALLOC(indexed_data);
|
|
||||||
if(indexed_data == NULL) {
|
|
||||||
LV_LOG_ERROR("Draw buffer alloc failed");
|
|
||||||
goto exit_with_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t data_len = 0;
|
|
||||||
if(lv_fs_seek(f, 0, LV_FS_SEEK_END) != LV_FS_RES_OK ||
|
|
||||||
lv_fs_tell(f, &data_len) != LV_FS_RES_OK) {
|
|
||||||
LV_LOG_WARN("Failed to get file to size");
|
|
||||||
goto exit_with_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t data_offset = sizeof(lv_image_header_t) + palette_len;
|
|
||||||
data_len -= data_offset;
|
|
||||||
res = fs_read_file_at(f, data_offset, (uint8_t *)indexed_data, data_len, &rn);
|
|
||||||
if(res != LV_FS_RES_OK || rn != data_len) {
|
|
||||||
LV_LOG_WARN("Read indexed image failed: %d", res);
|
|
||||||
goto exit_with_buf;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
|
||||||
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
|
|
||||||
palette = (lv_color32_t *)image->data;
|
|
||||||
indexed_data = image->data + palette_len;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->palette = palette;
|
|
||||||
dsc->palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf);
|
|
||||||
|
|
||||||
#if LV_BIN_DECODER_RAM_LOAD
|
|
||||||
/*Convert to ARGB8888, since sw renderer cannot render it directly even it's in RAM*/
|
|
||||||
stride = lv_draw_buf_width_to_stride(dsc->header.w, LV_COLOR_FORMAT_ARGB8888);
|
|
||||||
uint8_t * img_data = lv_draw_buf_malloc(stride * dsc->header.h, cf);
|
|
||||||
if(img_data == NULL) {
|
|
||||||
LV_LOG_ERROR("No memory for indexed image");
|
|
||||||
goto exit_with_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t * in = indexed_data;
|
|
||||||
uint8_t * out = img_data;
|
|
||||||
for(uint32_t y = 0; y < dsc->header.h; y++) {
|
|
||||||
decode_indexed_line(cf, dsc->palette, 0, 0, dsc->header.w, in, (lv_color32_t *)out);
|
|
||||||
in += dsc->header.stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->header.stride = stride;
|
|
||||||
dsc->header.cf = LV_COLOR_FORMAT_ARGB8888;
|
|
||||||
dsc->img_data = img_data;
|
|
||||||
decoder_data->img_data = img_data; /*Free when decoder closes*/
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
decoder_data->palette = (void *)palette; /*Free decoder data on close*/
|
|
||||||
lv_draw_buf_free((void *)indexed_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
exit_with_buf:
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
lv_free((void *)palette);
|
|
||||||
lv_draw_buf_free((void *)indexed_data);
|
|
||||||
}
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
#else
|
|
||||||
LV_UNUSED(stride);
|
|
||||||
LV_UNUSED(indexed_data);
|
|
||||||
/*It needs to be read by get_area_cb later*/
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LV_BIN_DECODER_RAM_LOAD
|
|
||||||
static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder);
|
|
||||||
lv_result_t res;
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data;
|
|
||||||
lv_fs_file_t * f = decoder_data->f;
|
|
||||||
lv_color_format_t cf = dsc->header.cf;
|
|
||||||
|
|
||||||
uint32_t len = dsc->header.stride * dsc->header.h;
|
|
||||||
if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
|
||||||
len += dsc->header.w * dsc->header.h * 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * img_data = lv_draw_buf_malloc(len, cf);
|
|
||||||
LV_ASSERT_MALLOC(img_data);
|
|
||||||
if(img_data == NULL) {
|
|
||||||
LV_LOG_ERROR("No memory for rgb file read");
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t rn;
|
|
||||||
res = fs_read_file_at(f, sizeof(lv_image_header_t), img_data, len, &rn);
|
|
||||||
if(res != LV_FS_RES_OK || rn != len) {
|
|
||||||
LV_LOG_WARN("Read rgb file failed: %d", res);
|
|
||||||
lv_draw_buf_free(img_data);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
dsc->img_data = img_data;
|
|
||||||
decoder_data->img_data = img_data; /*Free when decoder closes*/
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
|
||||||
{
|
|
||||||
LV_UNUSED(decoder);
|
|
||||||
lv_result_t res;
|
|
||||||
uint32_t rn;
|
|
||||||
lv_image_decoder_built_in_data_t * decoder_data = dsc->user_data;
|
|
||||||
uint8_t bpp = lv_color_format_get_bpp(dsc->header.cf);
|
|
||||||
uint32_t w = (dsc->header.stride * 8) / bpp;
|
|
||||||
uint32_t buf_stride = (w * 8 + 7) >> 3; /*stride for img_data*/
|
|
||||||
uint32_t buf_len = w * dsc->header.h; /*always decode to A8 format*/
|
|
||||||
uint8_t * img_data = lv_draw_buf_malloc(buf_len, dsc->header.cf);
|
|
||||||
uint32_t file_len = (uint32_t)dsc->header.stride * dsc->header.h;
|
|
||||||
|
|
||||||
LV_ASSERT_MALLOC(img_data);
|
|
||||||
if(img_data == NULL) {
|
|
||||||
LV_LOG_ERROR("Out of memory");
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
|
||||||
res = fs_read_file_at(decoder_data->f, sizeof(lv_image_header_t), img_data, file_len, &rn);
|
|
||||||
if(res != LV_FS_RES_OK || rn != file_len) {
|
|
||||||
LV_LOG_WARN("Read header failed: %d", res);
|
|
||||||
lv_draw_buf_free(img_data);
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
|
||||||
/*Copy from image data*/
|
|
||||||
lv_memcpy(img_data, ((lv_image_dsc_t *)dsc->src)->data, file_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dsc->header.cf != LV_COLOR_FORMAT_A8) {
|
|
||||||
/*Convert A1/2/4 to A8 from last pixel to first pixel*/
|
|
||||||
uint8_t * in = img_data + file_len - 1;
|
|
||||||
uint8_t * out = img_data + buf_len - 1;
|
|
||||||
uint8_t mask = (1 << bpp) - 1;
|
|
||||||
uint8_t shift = 0;
|
|
||||||
for(uint32_t i = 0; i < buf_len; i++) {
|
|
||||||
/**
|
|
||||||
* Rounding error:
|
|
||||||
* Take bpp = 4 as example, alpha value of 0x0 to 0x0F should be
|
|
||||||
* mapped to 0x00 to 0xFF, thus the equation should be below Equation 3.
|
|
||||||
*
|
|
||||||
* But it involves division and multiplication, which is slow. So, if
|
|
||||||
* we ignore the rounding errors, Equation1, 2 could be faster. But it
|
|
||||||
* will either has error when alpha is 0xff or 0x00.
|
|
||||||
*
|
|
||||||
* We use Equation 3 here for maximum accuracy.
|
|
||||||
*
|
|
||||||
* Equation 1: *out = ((*in >> shift) & mask) << (8 - bpp);
|
|
||||||
* Equation 2: *out = ((((*in >> shift) & mask) + 1) << (8 - bpp)) - 1;
|
|
||||||
* Equation 3: *out = ((*in >> shift) & mask) * 255 / ((1L << bpp) - 1) ;
|
|
||||||
*/
|
|
||||||
*out = ((*in >> shift) & mask) * 255L / ((1L << bpp) - 1) ;
|
|
||||||
shift += bpp;
|
|
||||||
if(shift >= 8) {
|
|
||||||
shift = 0;
|
|
||||||
in--;
|
|
||||||
}
|
|
||||||
out--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder_data->img_data = img_data;
|
|
||||||
dsc->img_data = img_data;
|
|
||||||
dsc->header.stride = buf_stride;
|
|
||||||
dsc->header.cf = LV_COLOR_FORMAT_A8;
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x,
|
|
||||||
int32_t y,
|
|
||||||
int32_t w_px, const uint8_t * in, lv_color32_t * out)
|
|
||||||
{
|
|
||||||
uint8_t px_size;
|
|
||||||
uint16_t mask;
|
|
||||||
|
|
||||||
out += w_px * y;
|
|
||||||
|
|
||||||
int32_t w_byte = 0;
|
|
||||||
int8_t shift = 0;
|
|
||||||
switch(color_format) {
|
|
||||||
case LV_COLOR_FORMAT_I1:
|
|
||||||
px_size = 1;
|
|
||||||
w_byte = (w_px + 7) >> 3; /*E.g. w = 20 -> w = 2 + 1*/
|
|
||||||
in += w_byte * y; /*First pixel*/
|
|
||||||
in += x / 8; /*8pixel per byte*/
|
|
||||||
shift = 7 - (x & 0x7);
|
|
||||||
break;
|
|
||||||
case LV_COLOR_FORMAT_I2:
|
|
||||||
px_size = 2;
|
|
||||||
w_byte = (w_px + 3) >> 2; /*E.g. w = 13 -> w = 3 + 1 (bytes)*/
|
|
||||||
in += w_byte * y; /*First pixel*/
|
|
||||||
in += x / 4; /*4pixel per byte*/
|
|
||||||
shift = 6 - 2 * (x & 0x3);
|
|
||||||
break;
|
|
||||||
case LV_COLOR_FORMAT_I4:
|
|
||||||
px_size = 4;
|
|
||||||
w_byte = (w_px + 1) >> 1; /*E.g. w = 13 -> w = 6 + 1 (bytes)*/
|
|
||||||
in += w_byte * y; /*First pixel*/
|
|
||||||
in += x / 2; /*2pixel per byte*/
|
|
||||||
shift = 4 - 4 * (x & 0x1);
|
|
||||||
break;
|
|
||||||
case LV_COLOR_FORMAT_I8:
|
|
||||||
px_size = 8;
|
|
||||||
w_byte = w_px;
|
|
||||||
in += w_byte * y; /*First pixel*/
|
|
||||||
in += x;
|
|
||||||
shift = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return LV_RESULT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
|
|
||||||
|
|
||||||
int32_t i;
|
|
||||||
for(i = 0; i < w_px; i++) {
|
|
||||||
uint8_t val_act = (*in >> shift) & mask;
|
|
||||||
out[i] = palette[val_act];
|
|
||||||
|
|
||||||
shift -= px_size;
|
|
||||||
if(shift < 0) {
|
|
||||||
shift = 8 - px_size;
|
|
||||||
in++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LV_RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t img_width_to_stride(lv_image_header_t * header)
|
static uint32_t img_width_to_stride(lv_image_header_t * header)
|
||||||
{
|
{
|
||||||
if(header->cf == LV_COLOR_FORMAT_RGB565A8) {
|
if(header->cf == LV_COLOR_FORMAT_RGB565A8) {
|
||||||
@@ -905,20 +275,3 @@ static uint32_t img_width_to_stride(lv_image_header_t * header)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br)
|
|
||||||
{
|
|
||||||
lv_fs_res_t res;
|
|
||||||
if(br) *br = 0;
|
|
||||||
|
|
||||||
res = lv_fs_seek(f, pos, LV_FS_SEEK_SET);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
res |= lv_fs_read(f, buff, btr, br);
|
|
||||||
if(res != LV_FS_RES_OK) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LV_FS_RES_OK;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -241,33 +241,6 @@ void lv_image_decoder_set_get_area_cb(lv_image_decoder_t * decoder, lv_image_dec
|
|||||||
*/
|
*/
|
||||||
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb);
|
void lv_image_decoder_set_close_cb(lv_image_decoder_t * decoder, lv_image_decoder_close_f_t close_cb);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get info about a built-in image
|
|
||||||
* @param decoder the decoder where this function belongs
|
|
||||||
* @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol
|
|
||||||
* @param header store the image data here
|
|
||||||
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
|
||||||
*/
|
|
||||||
lv_result_t lv_image_decoder_built_in_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header);
|
|
||||||
|
|
||||||
lv_result_t lv_image_decoder_built_in_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
|
||||||
const lv_area_t * full_area, lv_area_t * decoded_area);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a built in image
|
|
||||||
* @param decoder the decoder where this function belongs
|
|
||||||
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
|
|
||||||
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
|
||||||
*/
|
|
||||||
lv_result_t lv_image_decoder_built_in_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the pending decoding. Free resources etc.
|
|
||||||
* @param decoder pointer to the decoder the function associated with
|
|
||||||
* @param dsc pointer to decoder descriptor
|
|
||||||
*/
|
|
||||||
void lv_image_decoder_built_in_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* MACROS
|
* MACROS
|
||||||
**********************/
|
**********************/
|
||||||
|
|||||||
680
src/libs/bin_decoder/lv_bin_decoder.c
Normal file
680
src/libs/bin_decoder/lv_bin_decoder.c
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_image_decoder.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_bin_decoder.h"
|
||||||
|
#include "../../draw/lv_draw_image.h"
|
||||||
|
#include "../../draw/lv_draw_buf.h"
|
||||||
|
#include "../../stdlib/lv_string.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
lv_fs_file_t * f;
|
||||||
|
lv_color32_t * palette;
|
||||||
|
uint8_t * img_data;
|
||||||
|
lv_opa_t * opa;
|
||||||
|
} decoder_data_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
static decoder_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc);
|
||||||
|
static void free_decoder_data(lv_image_decoder_dsc_t * dsc);
|
||||||
|
static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
#if LV_BIN_DECODER_RAM_LOAD
|
||||||
|
static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
#endif
|
||||||
|
static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x,
|
||||||
|
int32_t w_px, const uint8_t * in, lv_color32_t * out);
|
||||||
|
|
||||||
|
static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the lvgl binary image decoder module
|
||||||
|
*/
|
||||||
|
void lv_bin_decoder_init(void)
|
||||||
|
{
|
||||||
|
lv_image_decoder_t * decoder;
|
||||||
|
|
||||||
|
decoder = lv_image_decoder_create();
|
||||||
|
LV_ASSERT_MALLOC(decoder);
|
||||||
|
if(decoder == NULL) {
|
||||||
|
LV_LOG_WARN("Out of memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_image_decoder_set_info_cb(decoder, lv_bin_decoder_info);
|
||||||
|
lv_image_decoder_set_open_cb(decoder, lv_bin_decoder_open);
|
||||||
|
lv_image_decoder_set_get_area_cb(decoder, lv_bin_decoder_get_area);
|
||||||
|
lv_image_decoder_set_close_cb(decoder, lv_bin_decoder_close);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info about a lvgl binary image
|
||||||
|
* @param decoder the decoder where this function belongs
|
||||||
|
* @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol
|
||||||
|
* @param header store the image data here
|
||||||
|
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
||||||
|
*/
|
||||||
|
lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder); /*Unused*/
|
||||||
|
|
||||||
|
lv_image_src_t src_type = lv_image_src_get_type(src);
|
||||||
|
if(src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||||
|
header->w = ((lv_image_dsc_t *)src)->header.w;
|
||||||
|
header->h = ((lv_image_dsc_t *)src)->header.h;
|
||||||
|
header->cf = ((lv_image_dsc_t *)src)->header.cf;
|
||||||
|
header->stride = ((lv_image_dsc_t *)src)->header.stride;
|
||||||
|
}
|
||||||
|
else if(src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
/*Support only "*.bin" files*/
|
||||||
|
if(strcmp(lv_fs_get_ext(src), "bin")) return LV_RESULT_INVALID;
|
||||||
|
|
||||||
|
lv_fs_file_t f;
|
||||||
|
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
|
||||||
|
if(res == LV_FS_RES_OK) {
|
||||||
|
uint32_t rn;
|
||||||
|
res = lv_fs_read(&f, header, sizeof(lv_image_header_t), &rn);
|
||||||
|
lv_fs_close(&f);
|
||||||
|
if(res != LV_FS_RES_OK || rn != sizeof(lv_image_header_t)) {
|
||||||
|
LV_LOG_WARN("Read file header failed: %d", res);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(src_type == LV_IMAGE_SRC_SYMBOL) {
|
||||||
|
/*The size depend on the font but it is unknown here. It should be handled outside of the
|
||||||
|
*function*/
|
||||||
|
header->w = 1;
|
||||||
|
header->h = 1;
|
||||||
|
/*Symbols always have transparent parts. Important because of cover check in the draw
|
||||||
|
*function. The actual value doesn't matter because lv_draw_label will draw it*/
|
||||||
|
header->cf = LV_COLOR_FORMAT_A8;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LV_LOG_WARN("Image get info found unknown src type");
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a lvgl binary image
|
||||||
|
* @param decoder the decoder where this function belongs
|
||||||
|
* @param dsc pointer to decoder descriptor. `src`, `color` are already initialized in it.
|
||||||
|
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
||||||
|
*/
|
||||||
|
lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder);
|
||||||
|
lv_fs_res_t res = LV_RESULT_INVALID;
|
||||||
|
|
||||||
|
/*Open the file if it's a file*/
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
/*Support only "*.bin" files*/
|
||||||
|
if(strcmp(lv_fs_get_ext(dsc->src), "bin")) return LV_RESULT_INVALID;
|
||||||
|
|
||||||
|
/*If the file was open successfully save the file descriptor*/
|
||||||
|
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||||
|
if(decoder_data == NULL) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->user_data = decoder_data;
|
||||||
|
lv_fs_file_t * f = lv_malloc(sizeof(*f));
|
||||||
|
if(f == NULL) {
|
||||||
|
free_decoder_data(dsc);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = lv_fs_open(f, dsc->src, LV_FS_MODE_RD);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
LV_LOG_WARN("Open file failed: %d", res);
|
||||||
|
lv_free(f);
|
||||||
|
free_decoder_data(dsc);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder_data->f = f;
|
||||||
|
|
||||||
|
lv_color_format_t cf = dsc->header.cf;
|
||||||
|
|
||||||
|
/*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/
|
||||||
|
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
||||||
|
res = decode_indexed(decoder, dsc);
|
||||||
|
}
|
||||||
|
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
|
||||||
|
res = decode_alpha_only(decoder, dsc);
|
||||||
|
}
|
||||||
|
#if LV_BIN_DECODER_RAM_LOAD
|
||||||
|
else if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888
|
||||||
|
|| cf == LV_COLOR_FORMAT_RGB888 || cf == LV_COLOR_FORMAT_RGB565) {
|
||||||
|
res = decode_rgb(decoder, dsc);
|
||||||
|
}
|
||||||
|
else if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
||||||
|
res = decode_rgb(decoder, dsc);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
else {
|
||||||
|
/* decode them in get_area_cb */
|
||||||
|
res = LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||||
|
/*The variables should have valid data*/
|
||||||
|
lv_image_dsc_t * img_dsc = (lv_image_dsc_t *)dsc->src;
|
||||||
|
if(img_dsc->data == NULL) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_color_format_t cf = img_dsc->header.cf;
|
||||||
|
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
||||||
|
/*Need decoder data to store converted image*/
|
||||||
|
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||||
|
if(decoder_data == NULL) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = decode_indexed(decoder, dsc);
|
||||||
|
}
|
||||||
|
else if(LV_COLOR_FORMAT_IS_ALPHA_ONLY(cf)) {
|
||||||
|
/*Alpha only image will need decoder data to store pointer to decoded image, to free it when decoder closes*/
|
||||||
|
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||||
|
if(decoder_data == NULL) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = decode_alpha_only(decoder, dsc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/*In case of uncompressed formats the image stored in the ROM/RAM.
|
||||||
|
*So simply give its pointer*/
|
||||||
|
dsc->img_data = ((lv_image_dsc_t *)dsc->src)->data;
|
||||||
|
res = LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res != LV_RESULT_OK) {
|
||||||
|
free_decoder_data(dsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the pending decoding. Free resources etc.
|
||||||
|
* @param decoder pointer to the decoder the function associated with
|
||||||
|
* @param dsc pointer to decoder descriptor
|
||||||
|
*/
|
||||||
|
void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder); /*Unused*/
|
||||||
|
free_decoder_data(dsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
||||||
|
const lv_area_t * full_area, lv_area_t * decoded_area)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder); /*Unused*/
|
||||||
|
|
||||||
|
lv_color_format_t cf = dsc->header.cf;
|
||||||
|
/*Check if cf is supported*/
|
||||||
|
|
||||||
|
bool supported = LV_COLOR_FORMAT_IS_INDEXED(cf)
|
||||||
|
|| cf == LV_COLOR_FORMAT_ARGB8888 \
|
||||||
|
|| cf == LV_COLOR_FORMAT_XRGB8888 \
|
||||||
|
|| cf == LV_COLOR_FORMAT_RGB888 \
|
||||||
|
|| cf == LV_COLOR_FORMAT_RGB565 \
|
||||||
|
|| cf == LV_COLOR_FORMAT_RGB565A8;
|
||||||
|
if(!supported) {
|
||||||
|
LV_LOG_WARN("CF: %d is not supported", cf);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_result_t res = LV_RESULT_INVALID;
|
||||||
|
decoder_data_t * decoder_data = dsc->user_data;
|
||||||
|
lv_fs_file_t * f = decoder_data->f;
|
||||||
|
uint32_t bpp = lv_color_format_get_bpp(cf);
|
||||||
|
int32_t w_px = lv_area_get_width(full_area);
|
||||||
|
uint8_t * img_data = NULL;
|
||||||
|
uint32_t offset = sizeof(lv_image_header_t); /*All image starts with image header*/
|
||||||
|
|
||||||
|
/*We only support read line by line for now*/
|
||||||
|
if(decoded_area->y1 == LV_COORD_MIN) {
|
||||||
|
/*Indexed image is converted to ARGB888*/
|
||||||
|
uint32_t len = LV_COLOR_FORMAT_IS_INDEXED(cf) ? sizeof(lv_color32_t) * 8 : bpp;
|
||||||
|
len = (len * w_px) / 8;
|
||||||
|
img_data = lv_draw_buf_malloc(len, cf);
|
||||||
|
LV_ASSERT_NULL(img_data);
|
||||||
|
if(img_data == NULL)
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
|
||||||
|
*decoded_area = *full_area;
|
||||||
|
decoded_area->y2 = decoded_area->y1;
|
||||||
|
decoder_data->img_data = img_data; /*Free on decoder close*/
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decoded_area->y1++;
|
||||||
|
decoded_area->y2++;
|
||||||
|
img_data = decoder_data->img_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(decoded_area->y1 > full_area->y2) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
||||||
|
int32_t x_fraction = decoded_area->x1 % (8 / bpp);
|
||||||
|
uint32_t len = (w_px * bpp + 7) / 8 + 1; /*10px for 1bpp may across 3bytes*/
|
||||||
|
uint8_t * buf = NULL;
|
||||||
|
offset += dsc->palette_size * 4; /*Skip palette*/
|
||||||
|
offset += decoded_area->y1 * dsc->header.stride;
|
||||||
|
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
buf = lv_malloc(len);
|
||||||
|
LV_ASSERT_NULL(buf);
|
||||||
|
if(buf == NULL)
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
|
||||||
|
res = fs_read_file_at(f, offset, buf, len, NULL);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
lv_free(buf);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const lv_image_dsc_t * image = dsc->src;
|
||||||
|
buf = (void *)(image->data + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_indexed_line(cf, dsc->palette, x_fraction, w_px, buf, (lv_color32_t *)img_data);
|
||||||
|
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) lv_free((void *)buf);
|
||||||
|
|
||||||
|
dsc->img_data = img_data; /*Return decoded image*/
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cf == LV_COLOR_FORMAT_ARGB8888 || cf == LV_COLOR_FORMAT_XRGB8888 || cf == LV_COLOR_FORMAT_RGB888
|
||||||
|
|| cf == LV_COLOR_FORMAT_RGB565) {
|
||||||
|
uint32_t len = (w_px * bpp) / 8;
|
||||||
|
offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/
|
||||||
|
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
||||||
|
res = fs_read_file_at(f, offset, img_data, len, NULL);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->img_data = img_data; /*Return decoded image*/
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
||||||
|
bpp = 16; /* RGB565 + A8 mask*/
|
||||||
|
uint32_t len = (w_px * bpp) / 8; /*map comes firstly*/
|
||||||
|
offset += decoded_area->y1 * dsc->header.w * bpp / 8; /*Move to y1*/
|
||||||
|
offset += decoded_area->x1 * bpp / 8; /*Move to x1*/
|
||||||
|
res = fs_read_file_at(f, offset, img_data, len, NULL);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Now the A8 mask*/
|
||||||
|
offset = sizeof(lv_image_header_t);
|
||||||
|
offset += dsc->header.h * dsc->header.w * bpp / 8; /*Move to A8 map*/
|
||||||
|
offset += decoded_area->y1 * dsc->header.w * 1; /*Move to y1*/
|
||||||
|
offset += decoded_area->x1 * 1; /*Move to x1*/
|
||||||
|
res = fs_read_file_at(f, offset, img_data + len, w_px * 1, NULL);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->img_data = img_data; /*Return decoded image*/
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static decoder_data_t * get_decoder_data(lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
decoder_data_t * data = dsc->user_data;
|
||||||
|
if(data == NULL) {
|
||||||
|
data = lv_malloc_zeroed(sizeof(decoder_data_t));
|
||||||
|
LV_ASSERT_MALLOC(data);
|
||||||
|
if(data == NULL) {
|
||||||
|
LV_LOG_ERROR("Out of memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->user_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_decoder_data(lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
decoder_data_t * decoder_data = dsc->user_data;
|
||||||
|
if(decoder_data) {
|
||||||
|
if(decoder_data->f) {
|
||||||
|
lv_fs_close(decoder_data->f);
|
||||||
|
lv_free(decoder_data->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_draw_buf_free(decoder_data->img_data);
|
||||||
|
lv_free(decoder_data->palette);
|
||||||
|
lv_free(decoder_data);
|
||||||
|
dsc->user_data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder); /*Unused*/
|
||||||
|
lv_result_t res;
|
||||||
|
uint32_t rn;
|
||||||
|
decoder_data_t * decoder_data = dsc->user_data;
|
||||||
|
lv_fs_file_t * f = decoder_data->f;
|
||||||
|
lv_color_format_t cf = dsc->header.cf;
|
||||||
|
uint32_t palette_len = sizeof(lv_color32_t) * LV_COLOR_INDEXED_PALETTE_SIZE(cf);
|
||||||
|
const lv_color32_t * palette;
|
||||||
|
const uint8_t * indexed_data = NULL;
|
||||||
|
uint32_t stride = dsc->header.stride;
|
||||||
|
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
/*read palette for indexed image*/
|
||||||
|
palette = lv_malloc(palette_len);
|
||||||
|
LV_ASSERT_MALLOC(palette);
|
||||||
|
if(palette == NULL) {
|
||||||
|
LV_LOG_ERROR("Out of memory");
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = fs_read_file_at(f, sizeof(lv_image_header_t), (uint8_t *)palette, palette_len, &rn);
|
||||||
|
if(res != LV_FS_RES_OK || rn != palette_len) {
|
||||||
|
LV_LOG_WARN("Read palette failed: %d", res);
|
||||||
|
lv_free((void *)palette);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LV_BIN_DECODER_RAM_LOAD
|
||||||
|
indexed_data = lv_draw_buf_malloc(stride * dsc->header.h, cf);
|
||||||
|
LV_ASSERT_MALLOC(indexed_data);
|
||||||
|
if(indexed_data == NULL) {
|
||||||
|
LV_LOG_ERROR("Draw buffer alloc failed");
|
||||||
|
goto exit_with_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t data_len = 0;
|
||||||
|
if(lv_fs_seek(f, 0, LV_FS_SEEK_END) != LV_FS_RES_OK ||
|
||||||
|
lv_fs_tell(f, &data_len) != LV_FS_RES_OK) {
|
||||||
|
LV_LOG_WARN("Failed to get file to size");
|
||||||
|
goto exit_with_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t data_offset = sizeof(lv_image_header_t) + palette_len;
|
||||||
|
data_len -= data_offset;
|
||||||
|
res = fs_read_file_at(f, data_offset, (uint8_t *)indexed_data, data_len, &rn);
|
||||||
|
if(res != LV_FS_RES_OK || rn != data_len) {
|
||||||
|
LV_LOG_WARN("Read indexed image failed: %d", res);
|
||||||
|
goto exit_with_buf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||||
|
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
|
||||||
|
palette = (lv_color32_t *)image->data;
|
||||||
|
indexed_data = image->data + palette_len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->palette = palette;
|
||||||
|
dsc->palette_size = LV_COLOR_INDEXED_PALETTE_SIZE(cf);
|
||||||
|
|
||||||
|
#if LV_BIN_DECODER_RAM_LOAD
|
||||||
|
/*Convert to ARGB8888, since sw renderer cannot render it directly even it's in RAM*/
|
||||||
|
stride = lv_draw_buf_width_to_stride(dsc->header.w, LV_COLOR_FORMAT_ARGB8888);
|
||||||
|
uint8_t * img_data = lv_draw_buf_malloc(stride * dsc->header.h, cf);
|
||||||
|
if(img_data == NULL) {
|
||||||
|
LV_LOG_ERROR("No memory for indexed image");
|
||||||
|
goto exit_with_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t * in = indexed_data;
|
||||||
|
uint8_t * out = img_data;
|
||||||
|
for(uint32_t y = 0; y < dsc->header.h; y++) {
|
||||||
|
decode_indexed_line(cf, dsc->palette, 0, dsc->header.w, in, (lv_color32_t *)out);
|
||||||
|
in += dsc->header.stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->header.stride = stride;
|
||||||
|
dsc->header.cf = LV_COLOR_FORMAT_ARGB8888;
|
||||||
|
dsc->img_data = img_data;
|
||||||
|
decoder_data->img_data = img_data; /*Free when decoder closes*/
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
decoder_data->palette = (void *)palette; /*Free decoder data on close*/
|
||||||
|
lv_draw_buf_free((void *)indexed_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
exit_with_buf:
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
lv_free((void *)palette);
|
||||||
|
lv_draw_buf_free((void *)indexed_data);
|
||||||
|
}
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
#else
|
||||||
|
LV_UNUSED(stride);
|
||||||
|
LV_UNUSED(indexed_data);
|
||||||
|
/*It needs to be read by get_area_cb later*/
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LV_BIN_DECODER_RAM_LOAD
|
||||||
|
static lv_result_t decode_rgb(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder);
|
||||||
|
lv_result_t res;
|
||||||
|
decoder_data_t * decoder_data = dsc->user_data;
|
||||||
|
lv_fs_file_t * f = decoder_data->f;
|
||||||
|
lv_color_format_t cf = dsc->header.cf;
|
||||||
|
|
||||||
|
uint32_t len = dsc->header.stride * dsc->header.h;
|
||||||
|
if(cf == LV_COLOR_FORMAT_RGB565A8) {
|
||||||
|
len += dsc->header.w * dsc->header.h * 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * img_data = lv_draw_buf_malloc(len, cf);
|
||||||
|
LV_ASSERT_MALLOC(img_data);
|
||||||
|
if(img_data == NULL) {
|
||||||
|
LV_LOG_ERROR("No memory for rgb file read");
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rn;
|
||||||
|
res = fs_read_file_at(f, sizeof(lv_image_header_t), img_data, len, &rn);
|
||||||
|
if(res != LV_FS_RES_OK || rn != len) {
|
||||||
|
LV_LOG_WARN("Read rgb file failed: %d", res);
|
||||||
|
lv_draw_buf_free(img_data);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->img_data = img_data;
|
||||||
|
decoder_data->img_data = img_data; /*Free when decoder closes*/
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||||
|
{
|
||||||
|
LV_UNUSED(decoder);
|
||||||
|
lv_result_t res;
|
||||||
|
uint32_t rn;
|
||||||
|
decoder_data_t * decoder_data = dsc->user_data;
|
||||||
|
uint8_t bpp = lv_color_format_get_bpp(dsc->header.cf);
|
||||||
|
uint32_t w = (dsc->header.stride * 8) / bpp;
|
||||||
|
uint32_t buf_stride = (w * 8 + 7) >> 3; /*stride for img_data*/
|
||||||
|
uint32_t buf_len = w * dsc->header.h; /*always decode to A8 format*/
|
||||||
|
uint8_t * img_data = lv_draw_buf_malloc(buf_len, dsc->header.cf);
|
||||||
|
uint32_t file_len = (uint32_t)dsc->header.stride * dsc->header.h;
|
||||||
|
|
||||||
|
LV_ASSERT_MALLOC(img_data);
|
||||||
|
if(img_data == NULL) {
|
||||||
|
LV_LOG_ERROR("Out of memory");
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||||
|
res = fs_read_file_at(decoder_data->f, sizeof(lv_image_header_t), img_data, file_len, &rn);
|
||||||
|
if(res != LV_FS_RES_OK || rn != file_len) {
|
||||||
|
LV_LOG_WARN("Read header failed: %d", res);
|
||||||
|
lv_draw_buf_free(img_data);
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||||
|
/*Copy from image data*/
|
||||||
|
lv_memcpy(img_data, ((lv_image_dsc_t *)dsc->src)->data, file_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->header.cf != LV_COLOR_FORMAT_A8) {
|
||||||
|
/*Convert A1/2/4 to A8 from last pixel to first pixel*/
|
||||||
|
uint8_t * in = img_data + file_len - 1;
|
||||||
|
uint8_t * out = img_data + buf_len - 1;
|
||||||
|
uint8_t mask = (1 << bpp) - 1;
|
||||||
|
uint8_t shift = 0;
|
||||||
|
for(uint32_t i = 0; i < buf_len; i++) {
|
||||||
|
/**
|
||||||
|
* Rounding error:
|
||||||
|
* Take bpp = 4 as example, alpha value of 0x0 to 0x0F should be
|
||||||
|
* mapped to 0x00 to 0xFF, thus the equation should be below Equation 3.
|
||||||
|
*
|
||||||
|
* But it involves division and multiplication, which is slow. So, if
|
||||||
|
* we ignore the rounding errors, Equation1, 2 could be faster. But it
|
||||||
|
* will either has error when alpha is 0xff or 0x00.
|
||||||
|
*
|
||||||
|
* We use Equation 3 here for maximum accuracy.
|
||||||
|
*
|
||||||
|
* Equation 1: *out = ((*in >> shift) & mask) << (8 - bpp);
|
||||||
|
* Equation 2: *out = ((((*in >> shift) & mask) + 1) << (8 - bpp)) - 1;
|
||||||
|
* Equation 3: *out = ((*in >> shift) & mask) * 255 / ((1L << bpp) - 1) ;
|
||||||
|
*/
|
||||||
|
*out = ((*in >> shift) & mask) * 255L / ((1L << bpp) - 1) ;
|
||||||
|
shift += bpp;
|
||||||
|
if(shift >= 8) {
|
||||||
|
shift = 0;
|
||||||
|
in--;
|
||||||
|
}
|
||||||
|
out--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder_data->img_data = img_data;
|
||||||
|
dsc->img_data = img_data;
|
||||||
|
dsc->header.stride = buf_stride;
|
||||||
|
dsc->header.cf = LV_COLOR_FORMAT_A8;
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_color32_t * palette, int32_t x,
|
||||||
|
int32_t w_px, const uint8_t * in, lv_color32_t * out)
|
||||||
|
{
|
||||||
|
uint8_t px_size;
|
||||||
|
uint16_t mask;
|
||||||
|
|
||||||
|
int8_t shift = 0;
|
||||||
|
switch(color_format) {
|
||||||
|
case LV_COLOR_FORMAT_I1:
|
||||||
|
px_size = 1;
|
||||||
|
in += x / 8; /*8pixel per byte*/
|
||||||
|
shift = 7 - (x & 0x7);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_I2:
|
||||||
|
px_size = 2;
|
||||||
|
in += x / 4; /*4pixel per byte*/
|
||||||
|
shift = 6 - 2 * (x & 0x3);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_I4:
|
||||||
|
px_size = 4;
|
||||||
|
in += x / 2; /*2pixel per byte*/
|
||||||
|
shift = 4 - 4 * (x & 0x1);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_I8:
|
||||||
|
px_size = 8;
|
||||||
|
in += x;
|
||||||
|
shift = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return LV_RESULT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = (1 << px_size) - 1; /*E.g. px_size = 2; mask = 0x03*/
|
||||||
|
|
||||||
|
int32_t i;
|
||||||
|
for(i = 0; i < w_px; i++) {
|
||||||
|
uint8_t val_act = (*in >> shift) & mask;
|
||||||
|
out[i] = palette[val_act];
|
||||||
|
|
||||||
|
shift -= px_size;
|
||||||
|
if(shift < 0) {
|
||||||
|
shift = 8 - px_size;
|
||||||
|
in++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LV_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buff, uint32_t btr, uint32_t * br)
|
||||||
|
{
|
||||||
|
lv_fs_res_t res;
|
||||||
|
if(br) *br = 0;
|
||||||
|
|
||||||
|
res = lv_fs_seek(f, pos, LV_FS_SEEK_SET);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res |= lv_fs_read(f, buff, btr, br);
|
||||||
|
if(res != LV_FS_RES_OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LV_FS_RES_OK;
|
||||||
|
}
|
||||||
70
src/libs/bin_decoder/lv_bin_decoder.h
Normal file
70
src/libs/bin_decoder/lv_bin_decoder.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_bin_decoder.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_BIN_DECODER_H
|
||||||
|
#define LV_BIN_DECODER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "../../draw/lv_image_decoder.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the binary image decoder module
|
||||||
|
*/
|
||||||
|
void lv_bin_decoder_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info about a lvgl binary image
|
||||||
|
* @param decoder the decoder where this function belongs
|
||||||
|
* @param src the image source: pointer to an `lv_image_dsc_t` variable, a file path or a symbol
|
||||||
|
* @param header store the image data here
|
||||||
|
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
||||||
|
*/
|
||||||
|
lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header);
|
||||||
|
|
||||||
|
lv_result_t lv_bin_decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
||||||
|
const lv_area_t * full_area, lv_area_t * decoded_area);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a lvgl binary image
|
||||||
|
* @param decoder the decoder where this function belongs
|
||||||
|
* @param dsc pointer to decoder descriptor. `src`, `style` are already initialized in it.
|
||||||
|
* @return LV_RESULT_OK: the info is successfully stored in `header`; LV_RESULT_INVALID: unknown format or other error.
|
||||||
|
*/
|
||||||
|
lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the pending decoding. Free resources etc.
|
||||||
|
* @param decoder pointer to the decoder the function associated with
|
||||||
|
* @param dsc pointer to decoder descriptor
|
||||||
|
*/
|
||||||
|
void lv_bin_decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_BIN_DECODER_H*/
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
*********************/
|
*********************/
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "lv_rle_decoder.h"
|
#include "lv_rle_decoder.h"
|
||||||
|
#include "../bin_decoder/lv_bin_decoder.h"
|
||||||
|
|
||||||
#if LV_USE_RLE
|
#if LV_USE_RLE
|
||||||
|
|
||||||
@@ -490,7 +491,7 @@ static lv_res_t decoder_open(lv_image_decoder_t * decoder,
|
|||||||
data->decoder_dsc.src_type = LV_IMAGE_SRC_VARIABLE;
|
data->decoder_dsc.src_type = LV_IMAGE_SRC_VARIABLE;
|
||||||
data->decoder_dsc.src = &data->img_dsc;
|
data->decoder_dsc.src = &data->img_dsc;
|
||||||
|
|
||||||
res = lv_image_decoder_built_in_open(decoder, &data->decoder_dsc);
|
res = lv_bin_decoder_open(decoder, &data->decoder_dsc);
|
||||||
if(res != LV_RES_OK) {
|
if(res != LV_RES_OK) {
|
||||||
lv_free(img_data);
|
lv_free(img_data);
|
||||||
lv_free(data);
|
lv_free(data);
|
||||||
@@ -507,7 +508,7 @@ static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t *
|
|||||||
{
|
{
|
||||||
if(dsc->user_data) {
|
if(dsc->user_data) {
|
||||||
lv_rle_decoder_data_t * decoder_data = dsc->user_data;
|
lv_rle_decoder_data_t * decoder_data = dsc->user_data;
|
||||||
lv_image_decoder_built_in_close(decoder, &decoder_data->decoder_dsc);
|
lv_bin_decoder_close(decoder, &decoder_data->decoder_dsc);
|
||||||
if(decoder_data->img_dsc.data)
|
if(decoder_data->img_dsc.data)
|
||||||
lv_free((void *)decoder_data->img_dsc.data);
|
lv_free((void *)decoder_data->img_dsc.data);
|
||||||
lv_free(dsc->user_data);
|
lv_free(dsc->user_data);
|
||||||
|
|||||||
@@ -248,6 +248,9 @@ static inline void lv_obj_move_background(lv_obj_t * obj)
|
|||||||
|
|
||||||
#define LV_ZOOM_NONE LV_SCALE_NONE
|
#define LV_ZOOM_NONE LV_SCALE_NONE
|
||||||
|
|
||||||
|
#define lv_image_decoder_built_in_open lv_bin_decoder_open
|
||||||
|
#define lv_image_decoder_built_in_close lv_bin_decoder_close
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* MACROS
|
* MACROS
|
||||||
**********************/
|
**********************/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "display/lv_display_private.h"
|
#include "display/lv_display_private.h"
|
||||||
#include "indev/lv_indev_private.h"
|
#include "indev/lv_indev_private.h"
|
||||||
#include "layouts/lv_layout.h"
|
#include "layouts/lv_layout.h"
|
||||||
|
#include "libs/bin_decoder/lv_bin_decoder.h"
|
||||||
#include "libs/bmp/lv_bmp.h"
|
#include "libs/bmp/lv_bmp.h"
|
||||||
#include "libs/ffmpeg/lv_ffmpeg.h"
|
#include "libs/ffmpeg/lv_ffmpeg.h"
|
||||||
#include "libs/freetype/lv_freetype.h"
|
#include "libs/freetype/lv_freetype.h"
|
||||||
@@ -178,6 +179,7 @@ void lv_init(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
_lv_image_decoder_init();
|
_lv_image_decoder_init();
|
||||||
|
lv_bin_decoder_init(); /*LVGL built-in binary image decoder*/
|
||||||
|
|
||||||
_lv_cache_init();
|
_lv_cache_init();
|
||||||
_lv_cache_builtin_init();
|
_lv_cache_builtin_init();
|
||||||
|
|||||||
Reference in New Issue
Block a user