mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-27 03:33:48 +08:00
feat(image): add custom RLE decoder (#4332)
Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
parent
ae65f2e683
commit
d73f260002
3
Kconfig
3
Kconfig
@ -1115,6 +1115,9 @@ menu "LVGL configuration"
|
||||
config LV_USE_GIF
|
||||
bool "GIF decoder library"
|
||||
|
||||
config LV_USE_RLE
|
||||
bool "RLE compressed bin image decoder library"
|
||||
|
||||
config LV_USE_QRCODE
|
||||
bool "QR code library"
|
||||
|
||||
|
@ -19,3 +19,4 @@
|
||||
barcode
|
||||
rlottie
|
||||
ffmpeg
|
||||
rle
|
||||
|
BIN
docs/libs/rle-compress-statistics.png
Normal file
BIN
docs/libs/rle-compress-statistics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
84
docs/libs/rle.rst
Normal file
84
docs/libs/rle.rst
Normal file
@ -0,0 +1,84 @@
|
||||
RLE Decoder
|
||||
===========
|
||||
|
||||
The RLE Decoder is a part of LVGL that is responsible for decoding RLE
|
||||
compressed original LVGL binary files. The file name suffix is always set
|
||||
to '.rle' and the file header is added with another 64-bit value that
|
||||
includes a 32-bit magic number and 32-bit RLE information.
|
||||
|
||||
The decoder supports both variable and file as image sources. The original
|
||||
binary data is directly decompressed to RAM, and further decoded by LVGL's
|
||||
built-in decoder.
|
||||
|
||||
Benefits
|
||||
--------
|
||||
|
||||
Based on test result from a watch project. Most of the images can be compressed
|
||||
to save more than 70% space as show in below statistic. It shows the file count
|
||||
of every compress level. For rare conditions, RLE compress may increase the file
|
||||
size if there's no large repetition in data.
|
||||
|
||||
.. image:: /libs/rle-compress-statistics.png
|
||||
:alt: RLE compress statistics from a watch project
|
||||
:align: center
|
||||
|
||||
|
||||
Theory
|
||||
------
|
||||
|
||||
The RLE algorithm is a simple compression algorithm that is based on the fact that
|
||||
the for many pixels, the color is the same. The algorithm simply counts how many
|
||||
repeated data are there and store the count value and the color value.
|
||||
If the coming pixels are not repeated, it stores the non-repeat count value and
|
||||
original color value. For more details, the script used to compress the image
|
||||
can be found from `lvgl/script/LVGLImage.py`.
|
||||
|
||||
.. code:: python
|
||||
|
||||
def rle_compress(self, data: bytearray, blksize: int, threshold=16):
|
||||
index = 0
|
||||
data_len = len(data)
|
||||
compressed_data = []
|
||||
while index < data_len:
|
||||
memview = memoryview(data)
|
||||
repeat_cnt = self.get_repeat_count(
|
||||
memview[index:], blksize)
|
||||
if repeat_cnt == 0:
|
||||
# done
|
||||
break
|
||||
elif repeat_cnt < threshold:
|
||||
nonrepeat_cnt = self.get_nonrepeat_count(
|
||||
memview[index:], blksize, threshold)
|
||||
ctrl_byte = uint8_t(nonrepeat_cnt | 0x80)
|
||||
compressed_data.append(ctrl_byte)
|
||||
compressed_data.append(
|
||||
memview[index: index + nonrepeat_cnt*blksize])
|
||||
index += nonrepeat_cnt * blksize
|
||||
else:
|
||||
ctrl_byte = uint8_t(repeat_cnt)
|
||||
compressed_data.append(ctrl_byte)
|
||||
compressed_data.append(memview[index: index + blksize])
|
||||
index += repeat_cnt * blksize
|
||||
|
||||
return b"".join(compressed_data)
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To use the RLE Decoder, ensure that `LV_USE_RLE` is defined and set to `1`.
|
||||
The RLE image can be used same as other images.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_image_set_src(img, "path/to/image.rle");
|
||||
|
||||
Generate RLE images
|
||||
-------------------
|
||||
|
||||
The image can be directly generated using script `lvgl/script/LVGLImage.py`
|
||||
|
||||
|
||||
.. code:: bash
|
||||
|
||||
./script/LVGLImage.py --ofmt RLE --cf I8 cogwheel.png
|
@ -81,7 +81,7 @@
|
||||
</release>
|
||||
<release date="2022-08-30" version="1.0.8" url="https://github.com/lvgl/lvgl/raw/eb2e296d23b009aca7daf0e9be062d05b4b0048a/env_support/cmsis-pack/LVGL.lvgl.1.0.8.pack">
|
||||
- LVGL 9.0.0-dev
|
||||
- Add the binding for pikascript (an ultra-light-weight python VM)
|
||||
- Add the binding for pikascript (an ultra-light-weight python VM)
|
||||
- Montyly update for August
|
||||
</release>
|
||||
<release date="2022-07-29" version="1.0.7" url="https://github.com/lvgl/lvgl/raw/b454a66e0be85976385c81cf9c9025f272a66f5d/env_support/cmsis-pack/LVGL.lvgl.1.0.7.pack">
|
||||
@ -212,7 +212,7 @@
|
||||
<description>Require Arm-2D Support</description>
|
||||
<require Cclass="Acceleration" Cgroup="Arm-2D"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-Arm-2D">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -223,7 +223,7 @@
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU NXP-VGLite"/>
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU GD32-IPA"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-STM32-DMA2D">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -234,7 +234,7 @@
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU NXP-VGLite"/>
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU GD32-IPA"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-SWM341-DMA2D">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -245,7 +245,7 @@
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU NXP-VGLite"/>
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU GD32-IPA"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-NXP-PXP">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -256,7 +256,7 @@
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU NXP-VGLite"/>
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU GD32-IPA"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-NXP-VGLite">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -267,7 +267,7 @@
|
||||
<!--<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU NXP-VGLite"/>-->
|
||||
<deny Cclass="LVGL" Cgroup="lvgl" Csub="GPU GD32-IPA"/>
|
||||
</condition>
|
||||
|
||||
|
||||
<condition id="LVGL-GPU-GD32-IPA">
|
||||
<description>Enable LVGL Arm-2D GPU Support</description>
|
||||
<require Cclass="LVGL" Cgroup="lvgl" Csub="Essential"/>
|
||||
@ -500,7 +500,7 @@
|
||||
<file category="sourceC" name="lv_cmsis_pack.c" attr="config" version="1.0.0" />
|
||||
<file category="header" name="lvgl.h" />
|
||||
<file category="doc" name="README.md"/>
|
||||
|
||||
|
||||
<!-- code template -->
|
||||
<file category="header" name="examples/porting/lv_port_disp_template.h" attr="template" select="Display port template" version="2.0.0"/>
|
||||
<file category="sourceC" name="examples/porting/lv_port_disp_template.c" attr="template" select="Display port template" version="2.0.0"/>
|
||||
@ -782,6 +782,21 @@
|
||||
#define LV_USE_GIF 1
|
||||
</RTE_Components_h>
|
||||
|
||||
/*! \brief enable RLE compressed bin image decoder support */
|
||||
#define LV_USE_RLE 1
|
||||
</RTE_Components_h>
|
||||
|
||||
</component>
|
||||
|
||||
<component Cgroup="lvgl" Csub="Libs RLE" condition="LVGL-Essential">
|
||||
<description>Add RLE compressed bin image decoder support</description>
|
||||
<files>
|
||||
<!-- src/libs/rle -->
|
||||
<file category="sourceC" name="src/libs/rle/lv_rle_decoder.c" />
|
||||
</files>
|
||||
|
||||
<RTE_Components_h>
|
||||
|
||||
</component>
|
||||
|
||||
<component Cgroup="lvgl" Csub="Libs sJPG" condition="LVGL-Essential">
|
||||
@ -799,7 +814,7 @@
|
||||
</RTE_Components_h>
|
||||
|
||||
</component>
|
||||
|
||||
|
||||
<component Cgroup="lvgl" Csub="Libs QRCode" condition="LVGL-Essential">
|
||||
<description>Add QRCode support</description>
|
||||
<files>
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
1. Copy the **lv_conf_template.h** to '**cmsis-pack**' directory
|
||||
|
||||
2. Set the macro protector to '1'
|
||||
2. Set the macro protector to '1'
|
||||
|
||||
```c
|
||||
...
|
||||
@ -47,7 +47,7 @@ remove the misleading guide above this code segment.
|
||||
#define LV_STRING_INCLUDE <string.h>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
5. Remove macro definitions for
|
||||
|
||||
@ -135,7 +135,7 @@ Make sure `LV_MEM_SIZE` is no less than `(64*1024U)`.
|
||||
#if LV_USE_DEMO_WIDGETS
|
||||
#define LV_DEMO_WIDGETS_SLIDESHOW 0
|
||||
#endif
|
||||
|
||||
|
||||
/*Benchmark your system*/
|
||||
#if LV_USE_DEMO_BENCHMARK
|
||||
/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/
|
||||
@ -143,7 +143,7 @@ Make sure `LV_MEM_SIZE` is no less than `(64*1024U)`.
|
||||
#endif
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
10. Remove following macro definitions in the `3rd party libraries` section:
|
||||
|
||||
@ -153,6 +153,7 @@ Make sure `LV_MEM_SIZE` is no less than `(64*1024U)`.
|
||||
- \#define LV_USE_FS_FATFS 0
|
||||
- \#define LV_USE_LODEPNG 0
|
||||
- \#define LV_USE_BMP 0
|
||||
- \#define LV_USE_RLE 0
|
||||
- \#define LV_USE_SJPG 0
|
||||
- \#define LV_USE_GIF 0
|
||||
- \#define LV_USE_BARCODE 0
|
||||
|
@ -619,6 +619,9 @@
|
||||
/*Decode bin images to RAM*/
|
||||
#define LV_BIN_DECODER_RAM_LOAD 0
|
||||
|
||||
/*RLE decoder library*/
|
||||
#define LV_USE_RLE 0
|
||||
|
||||
/*QR code library*/
|
||||
#define LV_USE_QRCODE 0
|
||||
|
||||
|
1
lvgl.h
1
lvgl.h
@ -91,6 +91,7 @@ extern "C" {
|
||||
|
||||
#include "src/libs/barcode/lv_barcode.h"
|
||||
#include "src/libs/bmp/lv_bmp.h"
|
||||
#include "src/libs/rle/lv_rle_decoder.h"
|
||||
#include "src/libs/fsdrv/lv_fsdrv.h"
|
||||
#include "src/libs/lodepng/lv_lodepng.h"
|
||||
#include "src/libs/libpng/lv_libpng.h"
|
||||
|
523
src/libs/rle/lv_rle_decoder.c
Normal file
523
src/libs/rle/lv_rle_decoder.c
Normal file
@ -0,0 +1,523 @@
|
||||
/**
|
||||
* @file lv_rle_decoder.c
|
||||
* Decode RLE compressed original lvgl binary file. File name suffix is always
|
||||
* set to '.rle' and the file header is added with another 64bit including
|
||||
* 32bit magic number and 32bit rle information.
|
||||
*
|
||||
* Decoder support both variable and file as image source.
|
||||
*
|
||||
* The original binary data is directly decompressed to ram, and decoded by
|
||||
* lvgl built-in decoder.
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include <stdlib.h>
|
||||
#include "lv_rle_decoder.h"
|
||||
|
||||
#if LV_USE_RLE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define RLE_HEADER_MAGIC 0x5aa521e0
|
||||
#define RLE_DECODER_OPTIMIZE_FS 1 /* load file to RAM to decode. */
|
||||
#define RLE_DECODER_PERF 0 /* output file load decoder performance */
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
lv_image_decoder_dsc_t decoder_dsc;
|
||||
lv_image_dsc_t img_dsc;
|
||||
} lv_rle_decoder_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* 0x5aa521e0 */
|
||||
uint32_t blksize: 4; /* block size, the encoded data unit bytes. */
|
||||
uint32_t len_orig: 24; /* Original data length. */
|
||||
uint32_t reserved: 4;
|
||||
} lv_rle_header_t;
|
||||
|
||||
typedef struct {
|
||||
lv_rle_header_t rleheader;
|
||||
lv_image_header_t header;
|
||||
} lv_rle_file_header_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static lv_res_t decoder_info(struct _lv_image_decoder_t * decoder,
|
||||
const void * src, lv_image_header_t * header);
|
||||
static lv_res_t decoder_open(lv_image_decoder_t * dec,
|
||||
lv_image_decoder_dsc_t * dsc);
|
||||
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_rle_decoder_init(void)
|
||||
{
|
||||
lv_image_decoder_t * dec = lv_image_decoder_create();
|
||||
lv_image_decoder_set_info_cb(dec, decoder_info);
|
||||
lv_image_decoder_set_open_cb(dec, decoder_open);
|
||||
lv_image_decoder_set_close_cb(dec, decoder_close);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#if RLE_DECODER_OPTIMIZE_FS == 0
|
||||
static int rle_decompress_from_file(lv_fs_file_t * f, uint8_t * output,
|
||||
uint32_t len, uint32_t blk_size)
|
||||
{
|
||||
uint8_t ctrl_byte;
|
||||
uint32_t wr_len = 0;
|
||||
lv_fs_res_t res;
|
||||
|
||||
while(1) {
|
||||
uint32_t rd;
|
||||
res = lv_fs_read(f, &ctrl_byte, 1, &rd);
|
||||
if(res != LV_FS_RES_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(rd == 0)
|
||||
break;
|
||||
|
||||
if(ctrl_byte & 0x80) {
|
||||
/* copy directly from input to output */
|
||||
uint32_t bytes = blk_size * (ctrl_byte & 0x7f);
|
||||
|
||||
wr_len += bytes;
|
||||
if(wr_len > len)
|
||||
return 0;
|
||||
|
||||
res = lv_fs_read(f, output, bytes, &rd);
|
||||
if(rd != bytes || res != LV_FS_RES_OK) {
|
||||
return 0;
|
||||
}
|
||||
output += bytes;
|
||||
}
|
||||
else {
|
||||
wr_len += blk_size * ctrl_byte;
|
||||
if(wr_len > len)
|
||||
return 0;
|
||||
|
||||
uint8_t input[blk_size];
|
||||
res = lv_fs_read(f, input, blk_size, &rd);
|
||||
if(rd != blk_size || res != LV_FS_RES_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(blk_size == 1) {
|
||||
/* optimize the most common case. */
|
||||
memset(output, input[0], ctrl_byte);
|
||||
output += ctrl_byte;
|
||||
}
|
||||
else {
|
||||
for(int i = 0; i < ctrl_byte; i++) {
|
||||
memcpy(output, input, blk_size);
|
||||
output += blk_size;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return wr_len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rle_decompress_from_mem(const uint8_t * input,
|
||||
uint32_t input_buff_len, uint8_t * output,
|
||||
uint32_t output_buff_len, uint8_t blk_size)
|
||||
{
|
||||
uint32_t ctrl_byte;
|
||||
uint32_t rd_len = 0;
|
||||
uint32_t wr_len = 0;
|
||||
|
||||
while(rd_len < input_buff_len) {
|
||||
ctrl_byte = input[0];
|
||||
rd_len++;
|
||||
input++;
|
||||
if(rd_len > input_buff_len)
|
||||
return 0;
|
||||
|
||||
if(ctrl_byte & 0x80) {
|
||||
/* copy directly from input to output */
|
||||
uint32_t bytes = blk_size * (ctrl_byte & 0x7f);
|
||||
rd_len += bytes;
|
||||
if(rd_len > input_buff_len)
|
||||
return 0;
|
||||
|
||||
wr_len += bytes;
|
||||
if(wr_len > output_buff_len)
|
||||
return 0;
|
||||
|
||||
memcpy(output, input, bytes);
|
||||
output += bytes;
|
||||
input += bytes;
|
||||
}
|
||||
else {
|
||||
rd_len += blk_size;
|
||||
if(rd_len > input_buff_len)
|
||||
return 0;
|
||||
|
||||
wr_len += blk_size * ctrl_byte;
|
||||
if(wr_len > output_buff_len)
|
||||
return 0;
|
||||
|
||||
if(blk_size == 1) {
|
||||
/* optimize the most common case. */
|
||||
memset(output, input[0], ctrl_byte);
|
||||
output += ctrl_byte;
|
||||
}
|
||||
else {
|
||||
for(uint32_t i = 0; i < ctrl_byte; i++) {
|
||||
memcpy(output, input, blk_size);
|
||||
output += blk_size;
|
||||
}
|
||||
}
|
||||
input += blk_size;
|
||||
}
|
||||
}
|
||||
|
||||
return wr_len;
|
||||
}
|
||||
|
||||
static lv_res_t decoder_info(struct _lv_image_decoder_t * decoder,
|
||||
const void * src, lv_image_header_t * header)
|
||||
{
|
||||
(void) decoder; /*Unused*/
|
||||
lv_image_src_t src_type = lv_image_src_get_type(src); /*Get the source type*/
|
||||
|
||||
if(src_type == LV_IMAGE_SRC_FILE) {
|
||||
lv_fs_res_t res;
|
||||
uint32_t rn;
|
||||
lv_fs_file_t f;
|
||||
lv_rle_file_header_t file_header = { 0 };
|
||||
/*Support only "*.rle" files*/
|
||||
if(strcmp(lv_fs_get_ext(src), "rle") != 0)
|
||||
return LV_RES_INV;
|
||||
|
||||
res = lv_fs_open(&f, src, LV_FS_MODE_RD);
|
||||
if(res != LV_FS_RES_OK)
|
||||
return LV_RES_INV;
|
||||
|
||||
res = lv_fs_read(&f, &file_header, sizeof(lv_rle_file_header_t), &rn);
|
||||
lv_fs_close(&f);
|
||||
|
||||
if((res != LV_FS_RES_OK) || (rn != sizeof(lv_rle_file_header_t))) {
|
||||
LV_LOG_WARN("Image get info get read file header");
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
if(file_header.rleheader.magic != RLE_HEADER_MAGIC) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
*header = file_header.header;
|
||||
|
||||
return LV_RES_OK;
|
||||
}
|
||||
else if(src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
const lv_image_dsc_t * img_dsc = src;
|
||||
const lv_rle_header_t * rleheader = (const lv_rle_header_t *)img_dsc->data;
|
||||
if(rleheader->magic != RLE_HEADER_MAGIC) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
header->always_zero = 0;
|
||||
header->cf = img_dsc->header.cf;
|
||||
header->w = img_dsc->header.w;
|
||||
header->h = img_dsc->header.h;
|
||||
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
static inline lv_res_t decode_from_file(lv_image_decoder_t * decoder,
|
||||
lv_image_decoder_dsc_t * dsc,
|
||||
lv_rle_file_header_t * fileheader,
|
||||
void ** img_data)
|
||||
{
|
||||
LV_UNUSED(decoder);
|
||||
lv_fs_res_t res;
|
||||
lv_fs_file_t f;
|
||||
uint32_t rd_cnt;
|
||||
uint32_t px_size;
|
||||
void * img_buf;
|
||||
uint32_t buf_len;
|
||||
uint32_t decoded_len;
|
||||
lv_image_header_t * header = &fileheader->header;
|
||||
lv_rle_header_t * rleheader = &fileheader->rleheader;
|
||||
const char * fn = dsc->src;
|
||||
|
||||
res = lv_fs_open(&f, fn, LV_FS_MODE_RD);
|
||||
if(res != LV_FS_RES_OK) {
|
||||
LV_LOG_WARN("RLE image decoder can't open the file");
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
res = lv_fs_read(&f, fileheader, sizeof(lv_rle_file_header_t), &rd_cnt);
|
||||
if(rleheader->magic != RLE_HEADER_MAGIC) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
if((res != LV_FS_RES_OK) || (rd_cnt != sizeof(lv_rle_file_header_t))) {
|
||||
LV_LOG_WARN("RLE image decoder read header failed.");
|
||||
goto error_file;
|
||||
}
|
||||
|
||||
px_size = lv_color_format_get_size(header->cf);
|
||||
if(px_size == 0) {
|
||||
goto error_file;
|
||||
}
|
||||
|
||||
if(px_size != rleheader->blksize) {
|
||||
LV_LOG_WARN("Invalid rle file, blksize mismatch, expect: %" LV_PRIu32", got %d", px_size, rleheader->blksize);
|
||||
goto error_file;
|
||||
}
|
||||
|
||||
buf_len = rleheader->len_orig;
|
||||
if(buf_len == 0) {
|
||||
LV_LOG_WARN("Invalid rle file, len_orig %d", rleheader->len_orig);
|
||||
goto error_file;
|
||||
}
|
||||
|
||||
img_buf = lv_draw_buf_malloc(buf_len, header->cf);
|
||||
if(img_buf == NULL) {
|
||||
LV_LOG_ERROR("alloc img buf failed: %" LV_PRId32, buf_len);
|
||||
goto error_file;
|
||||
}
|
||||
|
||||
#if RLE_DECODER_OPTIMIZE_FS
|
||||
uint32_t size = 0;
|
||||
if(lv_fs_seek(&f, 0, LV_FS_SEEK_END) != 0) {
|
||||
goto error_with_img_buf;
|
||||
}
|
||||
|
||||
lv_fs_tell(&f, &size);
|
||||
size -= sizeof(lv_rle_file_header_t);
|
||||
|
||||
if(lv_fs_seek(&f, sizeof(lv_rle_file_header_t), LV_FS_SEEK_SET) != 0) {
|
||||
goto error_with_img_buf;
|
||||
}
|
||||
|
||||
void * file_buffer = lv_malloc(size);
|
||||
if(file_buffer == NULL) {
|
||||
goto error_with_img_buf;
|
||||
}
|
||||
|
||||
#if RLE_DECODER_PERF
|
||||
int start, read_cost, decompress_cost;
|
||||
|
||||
start = lv_tick_get();
|
||||
#endif
|
||||
|
||||
res = lv_fs_read(&f, file_buffer, size, &rd_cnt);
|
||||
if(res != LV_RES_INV || rd_cnt != size) {
|
||||
lv_free(file_buffer);
|
||||
goto error_with_img_buf;
|
||||
}
|
||||
|
||||
lv_fs_close(&f);
|
||||
|
||||
#if RLE_DECODER_PERF
|
||||
read_cost = lv_tick_elaps(start);
|
||||
start = lv_tick_get();
|
||||
#endif
|
||||
|
||||
decoded_len = rle_decompress_from_mem(file_buffer, size, img_buf, buf_len,
|
||||
rleheader->blksize);
|
||||
|
||||
#if RLE_DECODER_PERF
|
||||
decompress_cost = lv_tick_elaps(start);
|
||||
LV_LOG_USER("file size: %" LV_PRIu32 ", img size: %" LV_PRIu32 ", readtime: %d, decode: %d\n",
|
||||
size, buf_len, read_cost, decompress_cost);
|
||||
#endif
|
||||
|
||||
lv_free(file_buffer);
|
||||
|
||||
if(decoded_len != buf_len) {
|
||||
LV_LOG_WARN("rle decode failed, decoded len: %" LV_PRIu32
|
||||
", expected %" LV_PRIu32 ".",
|
||||
decoded_len, buf_len);
|
||||
lv_free(img_buf);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Read and decompress data.
|
||||
*/
|
||||
decoded_len = rle_decompress_from_file(&f, img_buf, buf_len,
|
||||
rleheader->blksize);
|
||||
|
||||
lv_fs_close(&f);
|
||||
if(decoded_len != buf_len) {
|
||||
LV_LOG_WARN("rle decode failed, decoded len: %d, expect %d.",
|
||||
decoded_len, buf_len);
|
||||
lv_free(img_buf);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
#endif
|
||||
|
||||
*img_data = img_buf;
|
||||
return LV_RES_OK;
|
||||
|
||||
#if RLE_DECODER_OPTIMIZE_FS
|
||||
error_with_img_buf:
|
||||
lv_free(img_buf);
|
||||
#endif
|
||||
|
||||
error_file:
|
||||
lv_fs_close(&f);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
static inline lv_res_t decode_from_variable(lv_image_decoder_t * decoder,
|
||||
lv_image_decoder_dsc_t * dsc,
|
||||
lv_rle_file_header_t * fileheader,
|
||||
void ** img_data)
|
||||
{
|
||||
LV_UNUSED(decoder);
|
||||
uint32_t px_size;
|
||||
void * img_buf;
|
||||
uint32_t buf_len;
|
||||
uint32_t decoded_len;
|
||||
const lv_image_dsc_t * img_dsc = dsc->src;
|
||||
const lv_rle_header_t * rleheader = (const lv_rle_header_t *)img_dsc->data;
|
||||
const lv_image_header_t * header = &img_dsc->header;
|
||||
|
||||
if(rleheader->magic != RLE_HEADER_MAGIC) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
px_size = lv_color_format_get_size(header->cf);
|
||||
if(px_size == 0) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
if(px_size != rleheader->blksize) {
|
||||
LV_LOG_WARN("Invalid rle file, blksize mismatch, expect: %" LV_PRIu32
|
||||
", got %d",
|
||||
px_size, rleheader->blksize);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
buf_len = rleheader->len_orig;
|
||||
if(buf_len == 0) {
|
||||
LV_LOG_WARN("Invalid rle file, len_orig %d", rleheader->len_orig);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
img_buf = lv_malloc(buf_len);
|
||||
if(img_buf == NULL) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
const uint8_t * compressed_data = img_dsc->data + sizeof(lv_rle_header_t);
|
||||
uint32_t compressed_data_len = img_dsc->data_size - sizeof(lv_rle_header_t);
|
||||
|
||||
decoded_len = rle_decompress_from_mem(compressed_data, compressed_data_len,
|
||||
img_buf, buf_len, rleheader->blksize);
|
||||
|
||||
if(decoded_len != buf_len) {
|
||||
LV_LOG_WARN("rle decode failed, decoded len: %" LV_PRIu32
|
||||
", expected %" LV_PRIu32 ".",
|
||||
decoded_len, buf_len);
|
||||
lv_free(img_buf);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
fileheader->header = *header;
|
||||
fileheader->rleheader = *rleheader;
|
||||
*img_data = img_buf;
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
static lv_res_t decoder_open(lv_image_decoder_t * decoder,
|
||||
lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
lv_fs_res_t res;
|
||||
lv_rle_file_header_t fileheader;
|
||||
lv_rle_decoder_data_t * data;
|
||||
void * img_data;
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
res = decode_from_file(decoder, dsc, &fileheader, &img_data);
|
||||
if(res != LV_RES_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
|
||||
res = decode_from_variable(decoder, dsc, &fileheader, &img_data);
|
||||
|
||||
if(res != LV_RES_OK)
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
/*Decoder private data.*/
|
||||
data = lv_malloc(sizeof(*data));
|
||||
if(data == NULL) {
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
lv_memcpy(&data->decoder_dsc, dsc, sizeof(*dsc));
|
||||
|
||||
data->img_dsc.header = fileheader.header;
|
||||
data->img_dsc.data = img_data;
|
||||
data->img_dsc.data_size = fileheader.rleheader.len_orig;
|
||||
|
||||
data->decoder_dsc.img_data = NULL;
|
||||
data->decoder_dsc.src_type = LV_IMAGE_SRC_VARIABLE;
|
||||
data->decoder_dsc.src = &data->img_dsc;
|
||||
|
||||
res = lv_image_decoder_built_in_open(decoder, &data->decoder_dsc);
|
||||
if(res != LV_RES_OK) {
|
||||
lv_free(img_data);
|
||||
lv_free(data);
|
||||
return LV_RES_INV;
|
||||
}
|
||||
|
||||
dsc->img_data = data->decoder_dsc.img_data;
|
||||
dsc->header = data->decoder_dsc.header;
|
||||
dsc->user_data = data;
|
||||
return LV_RES_OK;
|
||||
}
|
||||
|
||||
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
if(dsc->user_data) {
|
||||
lv_rle_decoder_data_t * decoder_data = dsc->user_data;
|
||||
lv_image_decoder_built_in_close(decoder, &decoder_data->decoder_dsc);
|
||||
if(decoder_data->img_dsc.data)
|
||||
lv_free((void *)decoder_data->img_dsc.data);
|
||||
lv_free(dsc->user_data);
|
||||
dsc->user_data = NULL;
|
||||
}
|
||||
|
||||
if(dsc->img_data) {
|
||||
dsc->img_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*LV_USE_RLE*/
|
47
src/libs/rle/lv_rle_decoder.h
Normal file
47
src/libs/rle/lv_rle_decoder.h
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file lv_rle_decoder.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_RLE_DECODER_H
|
||||
#define LV_RLE_DECODER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../../lvgl.h"
|
||||
|
||||
#if LV_USE_RLE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Register the RLE decoder functions in LVGL
|
||||
*/
|
||||
void lv_rle_decoder_init(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_RLE*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*LV_RLE_DECODER_H*/
|
@ -2055,6 +2055,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*RLE decoder library*/
|
||||
#ifndef LV_USE_RLE
|
||||
#ifdef CONFIG_LV_USE_RLE
|
||||
#define LV_USE_RLE CONFIG_LV_USE_RLE
|
||||
#else
|
||||
#define LV_USE_RLE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*QR code library*/
|
||||
#ifndef LV_USE_QRCODE
|
||||
#ifdef CONFIG_LV_USE_QRCODE
|
||||
|
@ -258,6 +258,10 @@ void lv_init(void)
|
||||
lv_bmp_init();
|
||||
#endif
|
||||
|
||||
#if LV_USE_RLE
|
||||
lv_rle_decoder_init();
|
||||
#endif
|
||||
|
||||
/*Make FFMPEG last because the last converter will be checked first and
|
||||
*it's superior to any other */
|
||||
#if LV_USE_FFMPEG
|
||||
|
BIN
tests/ref_imgs/draw/image_format_rle.png
Normal file
BIN
tests/ref_imgs/draw/image_format_rle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
BIN
tests/ref_imgs/draw/image_format_rle_rotate.png
Normal file
BIN
tests/ref_imgs/draw/image_format_rle_rotate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
BIN
tests/ref_imgs/draw/image_format_rle_rotate_recolor.png
Normal file
BIN
tests/ref_imgs/draw/image_format_rle_rotate_recolor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
@ -60,6 +60,7 @@
|
||||
#define LV_USE_FS_MEMFS 1
|
||||
#define LV_FS_MEMFS_LETTER 'M'
|
||||
|
||||
#define LV_USE_RLE 1
|
||||
#define LV_USE_LODEPNG 1
|
||||
#define LV_USE_LIBPNG 1
|
||||
#define LV_USE_BMP 1
|
||||
|
@ -17,5 +17,6 @@
|
||||
|
||||
#define LV_USE_LODEPNG 1
|
||||
#define LV_USE_BMP 1
|
||||
#define LV_USE_RLE 1
|
||||
#define LV_USE_GIF 1
|
||||
#define LV_USE_QRCODE 1
|
||||
|
@ -157,4 +157,52 @@ void test_image_built_in_decode_rotate_and_recolor(void)
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rotate_and_recolor.png");
|
||||
}
|
||||
|
||||
void test_image_rle_decode(void)
|
||||
{
|
||||
img_create("rleA8", "A:src/test_files/binimages/cogwheel.A8.rle", false, false);
|
||||
img_create("rleI1", "A:src/test_files/binimages/cogwheel.I1.rle", false, false);
|
||||
img_create("rleI2", "A:src/test_files/binimages/cogwheel.I2.rle", false, false);
|
||||
img_create("rleI4", "A:src/test_files/binimages/cogwheel.I4.rle", false, false);
|
||||
img_create("rleI8", "A:src/test_files/binimages/cogwheel.I8.rle", false, false);
|
||||
img_create("rleRGB565A8", "A:src/test_files/binimages/cogwheel.RGB565A8.rle", false, false);
|
||||
img_create("rleRGB565", "A:src/test_files/binimages/cogwheel.RGB565.rle", false, false);
|
||||
img_create("rleRGB888", "A:src/test_files/binimages/cogwheel.RGB888.rle", false, false);
|
||||
img_create("rleXRGB8888", "A:src/test_files/binimages/cogwheel.XRGB8888.rle", false, false);
|
||||
img_create("rleARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.rle", false, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle.png");
|
||||
}
|
||||
|
||||
void test_image_rle_decode_rotate(void)
|
||||
{
|
||||
img_create("rleA8", "A:src/test_files/binimages/cogwheel.A8.rle", true, false);
|
||||
img_create("rleI1", "A:src/test_files/binimages/cogwheel.I1.rle", true, false);
|
||||
img_create("rleI2", "A:src/test_files/binimages/cogwheel.I2.rle", true, false);
|
||||
img_create("rleI4", "A:src/test_files/binimages/cogwheel.I4.rle", true, false);
|
||||
img_create("rleI8", "A:src/test_files/binimages/cogwheel.I8.rle", true, false);
|
||||
img_create("rleRGB565A8", "A:src/test_files/binimages/cogwheel.RGB565A8.rle", true, false);
|
||||
img_create("rleRGB565", "A:src/test_files/binimages/cogwheel.RGB565.rle", true, false);
|
||||
img_create("rleRGB888", "A:src/test_files/binimages/cogwheel.RGB888.rle", true, false);
|
||||
img_create("rleXRGB8888", "A:src/test_files/binimages/cogwheel.XRGB8888.rle", true, false);
|
||||
img_create("rleARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.rle", true, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_rotate.png");
|
||||
}
|
||||
|
||||
void test_image_rle_decode_rotate_recolor(void)
|
||||
{
|
||||
img_create("rleA8", "A:src/test_files/binimages/cogwheel.A8.rle", true, true);
|
||||
img_create("rleI1", "A:src/test_files/binimages/cogwheel.I1.rle", true, true);
|
||||
img_create("rleI2", "A:src/test_files/binimages/cogwheel.I2.rle", true, true);
|
||||
img_create("rleI4", "A:src/test_files/binimages/cogwheel.I4.rle", true, true);
|
||||
img_create("rleI8", "A:src/test_files/binimages/cogwheel.I8.rle", true, true);
|
||||
img_create("rleRGB565A8", "A:src/test_files/binimages/cogwheel.RGB565A8.rle", true, true);
|
||||
img_create("rleRGB565", "A:src/test_files/binimages/cogwheel.RGB565.rle", true, true);
|
||||
img_create("rleRGB888", "A:src/test_files/binimages/cogwheel.RGB888.rle", true, true);
|
||||
img_create("rleXRGB8888", "A:src/test_files/binimages/cogwheel.XRGB8888.rle", true, true);
|
||||
img_create("rleARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.rle", true, true);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_rotate_recolor.png");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
BIN
tests/src/test_files/binimages/cogwheel.A1.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.A1.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.A2.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.A2.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.A4.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.A4.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.A8.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.A8.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.ARGB8888.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.ARGB8888.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.I1.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.I1.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.I2.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.I2.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.I4.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.I4.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.I8.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.I8.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.RGB565.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.RGB565.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.RGB565A8.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.RGB565A8.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.RGB888.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.RGB888.rle
Normal file
Binary file not shown.
BIN
tests/src/test_files/binimages/cogwheel.XRGB8888.rle
Normal file
BIN
tests/src/test_files/binimages/cogwheel.XRGB8888.rle
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user