mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-27 19:53:42 +08:00
feat(rle): add RLE compressed binary image support (#4870)
Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
parent
5d38a26a8a
commit
16254ef90d
@ -91,6 +91,11 @@ class PngQuant:
|
||||
return compressed
|
||||
|
||||
|
||||
class CompressMethod(Enum):
|
||||
NONE = 0x00
|
||||
RLE = 0x01
|
||||
|
||||
|
||||
class ColorFormat(Enum):
|
||||
UNKNOWN = 0x00
|
||||
TRUECOLOR = 0x04
|
||||
@ -280,8 +285,10 @@ class LVGLImageHeader:
|
||||
w: int = 0,
|
||||
h: int = 0,
|
||||
stride: int = 0,
|
||||
align: int = 1):
|
||||
align: int = 1,
|
||||
flags: int = 0):
|
||||
self.cf = cf
|
||||
self.flags = flags
|
||||
self.w = w & 0xffff
|
||||
self.h = h & 0xffff
|
||||
if w > 0xffff or h > 0xffff:
|
||||
@ -314,8 +321,7 @@ class LVGLImageHeader:
|
||||
binary = bytearray()
|
||||
binary += uint8_t(self.cf.value)
|
||||
binary += uint8_t(0) # 8bits format
|
||||
binary += uint8_t(0) # 8bits user flags
|
||||
binary += uint8_t(0) # 8bits reserved
|
||||
binary += uint16_t(self.flags) # 16bits flags
|
||||
|
||||
binary += uint16_t(self.w) # 16bits width
|
||||
binary += uint16_t(self.h) # 16bits height
|
||||
@ -338,6 +344,40 @@ class LVGLImageHeader:
|
||||
return self
|
||||
|
||||
|
||||
class LVGLCompressData:
|
||||
|
||||
def __init__(self,
|
||||
cf: ColorFormat,
|
||||
method: CompressMethod,
|
||||
raw_data: bytes = b''):
|
||||
self.blk_size = (cf.bpp + 7) // 8
|
||||
self.compress = method
|
||||
self.raw_data = raw_data
|
||||
self.raw_data_len = len(raw_data)
|
||||
self.compressed = self._compress(raw_data)
|
||||
|
||||
def _compress(self, raw_data: bytes) -> bytearray:
|
||||
if self.compress == CompressMethod.NONE:
|
||||
return raw_data
|
||||
|
||||
if self.compress == CompressMethod.RLE:
|
||||
# RLE compression performs on pixel unit, pad data to pixel unit
|
||||
pad = b'\x00' * (self.blk_size - self.raw_data_len % self.blk_size)
|
||||
self.raw_data_len += len(pad)
|
||||
compressed = RLEImage().rle_compress(raw_data + pad, self.blk_size)
|
||||
else:
|
||||
raise ParameterError(f"Invalid compress method: {self.compress}")
|
||||
|
||||
self.compressed_len = len(compressed)
|
||||
|
||||
bin = bytearray()
|
||||
bin += uint32_t(self.compress.value)
|
||||
bin += uint32_t(self.compressed_len)
|
||||
bin += uint32_t(self.raw_data_len)
|
||||
bin += compressed
|
||||
return bin
|
||||
|
||||
|
||||
class LVGLImage:
|
||||
|
||||
def __init__(self,
|
||||
@ -346,12 +386,17 @@ class LVGLImage:
|
||||
h: int = 0,
|
||||
data: bytes = b'') -> None:
|
||||
self.stride = 0 # default no valid stride value
|
||||
self.compress = CompressMethod.NONE
|
||||
self.set_data(cf, w, h, data)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (f"'LVGL image {self.w}x{self.h}, {self.cf.name},"
|
||||
f" (12+{self.data_len})Byte'")
|
||||
|
||||
def set_compress(self, method: CompressMethod):
|
||||
# only do compression when return binary data
|
||||
self.compress = method
|
||||
|
||||
def adjust_stride(self, stride: int = 0, align: int = 1):
|
||||
'''
|
||||
Stride can be set directly, or by stride alignment in bytes
|
||||
@ -439,9 +484,17 @@ class LVGLImage:
|
||||
raw data
|
||||
'''
|
||||
bin = bytearray()
|
||||
header = LVGLImageHeader(self.cf, self.w, self.h, self.stride)
|
||||
flags = 0
|
||||
flags |= 0x08 if self.compress != CompressMethod.NONE else 0
|
||||
|
||||
header = LVGLImageHeader(self.cf,
|
||||
self.w,
|
||||
self.h,
|
||||
self.stride,
|
||||
flags=flags)
|
||||
bin += header.binary
|
||||
bin += self.data
|
||||
compress = LVGLCompressData(self.cf, self.compress, self.data)
|
||||
bin += compress.compressed
|
||||
return bin
|
||||
|
||||
@property
|
||||
@ -614,7 +667,7 @@ const lv_img_dsc_t {varname} = {{
|
||||
data += [0, 0, 0, a]
|
||||
encoder = png.Writer(self.w, self.h, greyscale=False, alpha=True)
|
||||
elif self.cf == ColorFormat.L8:
|
||||
# to greyscale
|
||||
# to grayscale
|
||||
encoder = png.Writer(self.w,
|
||||
self.h,
|
||||
bitdepth=self.cf.bpp,
|
||||
@ -634,7 +687,7 @@ const lv_img_dsc_t {varname} = {{
|
||||
with open(filename, "wb") as f:
|
||||
encoder.write_array(f, data)
|
||||
|
||||
self.adjust_stride(stride=self.stride)
|
||||
self.adjust_stride(stride=old_stride)
|
||||
|
||||
def from_png(self,
|
||||
filename: str,
|
||||
@ -852,8 +905,8 @@ class RLEImage(LVGLImage):
|
||||
index = 0
|
||||
data_len = len(data)
|
||||
compressed_data = []
|
||||
memview = memoryview(data)
|
||||
while index < data_len:
|
||||
memview = memoryview(data)
|
||||
repeat_cnt = self.get_repeat_count(memview[index:], blksize)
|
||||
if repeat_cnt == 0:
|
||||
# done
|
||||
@ -946,6 +999,7 @@ class PNGConverter:
|
||||
odir: str,
|
||||
background: int = 0x00,
|
||||
align: int = 1,
|
||||
compress: CompressMethod = CompressMethod.NONE,
|
||||
keep_folder=True) -> None:
|
||||
self.files = files
|
||||
self.cf = cf
|
||||
@ -954,6 +1008,7 @@ class PNGConverter:
|
||||
self.pngquant = None
|
||||
self.keep_folder = keep_folder
|
||||
self.align = align
|
||||
self.compress = compress
|
||||
self.background = background
|
||||
|
||||
def _replace_ext(self, input, ext):
|
||||
@ -974,6 +1029,7 @@ class PNGConverter:
|
||||
self.cf,
|
||||
background=self.background)
|
||||
rle.adjust_stride(align=self.align)
|
||||
rle.set_compress(self.compress)
|
||||
output.append((f, rle))
|
||||
rle.to_rle(self._replace_ext(f, ".rle"))
|
||||
else:
|
||||
@ -981,6 +1037,7 @@ class PNGConverter:
|
||||
self.cf,
|
||||
background=self.background)
|
||||
img.adjust_stride(align=self.align)
|
||||
img.set_compress(self.compress)
|
||||
output.append((f, img))
|
||||
if self.ofmt == OutputFormat.BIN_FILE:
|
||||
img.to_bin(self._replace_ext(f, ".bin"))
|
||||
@ -1008,6 +1065,12 @@ def main():
|
||||
"XRGB8888", "RGB565", "RGB565A8", "RGB888", "TRUECOLOR",
|
||||
"TRUECOLOR_ALPHA", "AUTO"
|
||||
])
|
||||
|
||||
parser.add_argument('--compress',
|
||||
help=("Binary data compress method, default to NONE"),
|
||||
default="NONE",
|
||||
choices=["NONE", "RLE"])
|
||||
|
||||
parser.add_argument('--align',
|
||||
help="stride alignment in bytes for bin image",
|
||||
default=1,
|
||||
@ -1048,6 +1111,7 @@ def main():
|
||||
cf = ColorFormat[args.cf]
|
||||
|
||||
ofmt = OutputFormat(args.ofmt)
|
||||
compress = CompressMethod[args.compress]
|
||||
|
||||
converter = PNGConverter(files,
|
||||
cf,
|
||||
@ -1055,6 +1119,7 @@ def main():
|
||||
args.output,
|
||||
background=args.background,
|
||||
align=args.align,
|
||||
compress=compress,
|
||||
keep_folder=False)
|
||||
output = converter.convert()
|
||||
for f, img in output:
|
||||
@ -1066,10 +1131,11 @@ def main():
|
||||
def test():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
f = "pngs/cogwheel.RGB565A8.png"
|
||||
img = LVGLImage().from_png(f, cf=ColorFormat.RGB565, background=0xFF_FF_00)
|
||||
img = LVGLImage().from_png(f, cf=ColorFormat.RGB888, background=0xFF_FF_00)
|
||||
img.adjust_stride(align=16)
|
||||
img.to_bin(f + ".bin")
|
||||
img.to_png(f + ".png") # convert back to png
|
||||
img.set_compress(CompressMethod.RLE)
|
||||
img.to_bin("output/cogwheel.RGB888.bin")
|
||||
img.to_png("output/cogwheel.RGB888.png.png") # convert back to png
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -62,6 +62,13 @@ typedef enum _lv_image_flags_t {
|
||||
*/
|
||||
LV_IMAGE_FLAGS_VECTORS = 0x04,
|
||||
|
||||
/**
|
||||
* The image data is compressed, so decoder needs to decode image firstly.
|
||||
* If this flag is set, the whole image will be decompressed upon decode, and
|
||||
* `get_area_cb` won't be necessary.
|
||||
*/
|
||||
LV_IMAGE_FLAGS_COMPRESSED = 0x08,
|
||||
|
||||
/**
|
||||
* Flags reserved for user, lvgl won't use these bits.
|
||||
*/
|
||||
@ -75,6 +82,12 @@ typedef enum _lv_image_flags_t {
|
||||
LV_IMAGE_FLAGS_USER8 = 0x0800,
|
||||
} lv_image_flags_t;
|
||||
|
||||
typedef enum {
|
||||
LV_IMAGE_COMPRESS_NONE = 0,
|
||||
LV_IMAGE_COMPRESS_RLE, /*LVGL custom RLE compression*/
|
||||
LV_IMAGE_COMPRESS_LZ4,
|
||||
} lv_image_compress_t;
|
||||
|
||||
/**
|
||||
* The first 8 bit is very important to distinguish the different source types.
|
||||
* For more info see `lv_image_get_src_type()` in lv_img.c
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include "../../draw/lv_draw_image.h"
|
||||
#include "../../draw/lv_draw_buf.h"
|
||||
#include "../../stdlib/lv_string.h"
|
||||
#include "../../stdlib/lv_sprintf.h"
|
||||
#include "../../libs/rle/lv_rle.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@ -19,11 +21,25 @@
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Data format for compressed image data.
|
||||
*/
|
||||
|
||||
typedef struct _lv_image_compressed_t {
|
||||
uint32_t method: 4; /*Compression method, see `lv_image_compress_t`*/
|
||||
uint32_t reserved : 28; /*Reserved to be used later*/
|
||||
uint32_t compressed_size; /*Compressed data size in byte*/
|
||||
uint32_t decompressed_size; /*Decompressed data size in byte*/
|
||||
const uint8_t * data; /*Compressed data*/
|
||||
} lv_image_compressed_t;
|
||||
|
||||
typedef struct {
|
||||
lv_fs_file_t * f;
|
||||
lv_color32_t * palette;
|
||||
uint8_t * img_data;
|
||||
lv_opa_t * opa;
|
||||
uint8_t * decompressed;
|
||||
lv_image_compressed_t compressed;
|
||||
} decoder_data_t;
|
||||
|
||||
/**********************
|
||||
@ -38,8 +54,11 @@ static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder
|
||||
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_result_t decode_compressed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
|
||||
|
||||
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 lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, void * buff, uint32_t btr, uint32_t * br);
|
||||
|
||||
static lv_result_t decompress_image(lv_image_decoder_dsc_t * dsc, const lv_image_compressed_t * compressed);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
@ -86,10 +105,8 @@ lv_result_t lv_bin_decoder_info(lv_image_decoder_t * decoder, const void * src,
|
||||
|
||||
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;
|
||||
lv_image_dsc_t * image = (lv_image_dsc_t *)src;
|
||||
lv_memcpy(header, &image->header, sizeof(lv_image_header_t));
|
||||
}
|
||||
else if(src_type == LV_IMAGE_SRC_FILE) {
|
||||
/*Support only "*.bin" files*/
|
||||
@ -170,23 +187,26 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
decoder_data->f = f;
|
||||
decoder_data->f = f; /*Now free_decoder_data will take care of the file*/
|
||||
|
||||
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)) {
|
||||
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
|
||||
res = decode_compressed(decoder, dsc);
|
||||
}
|
||||
else if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
||||
/*Palette for indexed image and whole image of A8 image are always loaded to RAM for simplicity*/
|
||||
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) {
|
||||
else if(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) {
|
||||
res = decode_rgb(decoder, dsc);
|
||||
}
|
||||
#else
|
||||
@ -199,12 +219,16 @@ lv_result_t lv_bin_decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
|
||||
|
||||
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) {
|
||||
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
|
||||
if(image->data == NULL) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_color_format_t cf = img_dsc->header.cf;
|
||||
lv_color_format_t cf = image->header.cf;
|
||||
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
|
||||
/*@todo*/
|
||||
res = LV_RESULT_INVALID;
|
||||
}
|
||||
if(LV_COLOR_FORMAT_IS_INDEXED(cf)) {
|
||||
/*Need decoder data to store converted image*/
|
||||
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||
@ -404,6 +428,7 @@ static void free_decoder_data(lv_image_decoder_dsc_t * dsc)
|
||||
}
|
||||
|
||||
lv_draw_buf_free(decoder_data->img_data);
|
||||
lv_draw_buf_free(decoder_data->decompressed);
|
||||
lv_free(decoder_data->palette);
|
||||
lv_free(decoder_data);
|
||||
dsc->user_data = NULL;
|
||||
@ -423,7 +448,13 @@ static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder
|
||||
const uint8_t * indexed_data = NULL;
|
||||
uint32_t stride = dsc->header.stride;
|
||||
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
bool is_compressed = dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED;
|
||||
if(is_compressed) {
|
||||
uint8_t * data = decoder_data->decompressed;
|
||||
palette = (lv_color32_t *)data;
|
||||
indexed_data = data + palette_len;
|
||||
}
|
||||
else if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
/*read palette for indexed image*/
|
||||
palette = lv_malloc(palette_len);
|
||||
LV_ASSERT_MALLOC(palette);
|
||||
@ -496,14 +527,14 @@ static lv_result_t decode_indexed(lv_image_decoder_t * decoder, lv_image_decoder
|
||||
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) {
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE && !is_compressed) {
|
||||
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) {
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE && !is_compressed) {
|
||||
lv_free((void *)palette);
|
||||
lv_draw_buf_free((void *)indexed_data);
|
||||
}
|
||||
@ -570,7 +601,11 @@ static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_deco
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
if(dsc->src_type == LV_IMAGE_SRC_FILE) {
|
||||
if(dsc->header.flags & LV_IMAGE_FLAGS_COMPRESSED) {
|
||||
/*Copy from image data*/
|
||||
lv_memcpy(img_data, decoder_data->decompressed, file_len);
|
||||
}
|
||||
else 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);
|
||||
@ -622,6 +657,87 @@ static lv_result_t decode_alpha_only(lv_image_decoder_t * decoder, lv_image_deco
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
static lv_result_t decode_compressed(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
uint32_t rn;
|
||||
uint32_t len;
|
||||
uint32_t compressed_len;
|
||||
uint8_t * file_buf;
|
||||
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||
lv_fs_file_t * f = decoder_data->f;
|
||||
lv_result_t res;
|
||||
lv_image_compressed_t * compressed = &decoder_data->compressed;
|
||||
lv_memzero(compressed, sizeof(lv_image_compressed_t));
|
||||
|
||||
if(lv_fs_seek(f, 0, LV_FS_SEEK_END) != LV_FS_RES_OK ||
|
||||
lv_fs_tell(f, &compressed_len) != LV_FS_RES_OK) {
|
||||
LV_LOG_WARN("Failed to get compressed file len");
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
compressed_len -= sizeof(lv_image_header_t);
|
||||
compressed_len -= 12;
|
||||
|
||||
/*Read compress header*/
|
||||
len = 12;
|
||||
res = fs_read_file_at(f, sizeof(lv_image_header_t), compressed, len, &rn);
|
||||
if(res != LV_FS_RES_OK || rn != len) {
|
||||
LV_LOG_WARN("Read compressed header failed: %d", res);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
if(compressed->compressed_size != compressed_len) {
|
||||
LV_LOG_WARN("Compressed size mismatch: %" LV_PRIu32" != %" LV_PRIu32, compressed->compressed_size, compressed_len);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
file_buf = lv_malloc(compressed_len);
|
||||
if(file_buf == NULL) {
|
||||
LV_LOG_WARN("No memory for compressed file");
|
||||
return LV_RESULT_INVALID;
|
||||
|
||||
}
|
||||
|
||||
/*Continue to read the compressed data following compression header*/
|
||||
res = lv_fs_read(f, file_buf, compressed_len, &rn);
|
||||
if(res != LV_FS_RES_OK || rn != compressed_len) {
|
||||
LV_LOG_WARN("Read compressed file failed: %d", res);
|
||||
lv_free(file_buf);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/*Decompress the image*/
|
||||
compressed->data = file_buf;
|
||||
res = decompress_image(dsc, compressed);
|
||||
compressed->data = NULL; /*No need to store the data any more*/
|
||||
lv_free(file_buf);
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_WARN("Decompress failed");
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/*Depends on the cf, need to further decode image like an C-array image*/
|
||||
lv_image_dsc_t * image = (lv_image_dsc_t *)dsc->src;
|
||||
if(image->data == NULL) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_color_format_t cf = dsc->header.cf;
|
||||
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);
|
||||
}
|
||||
else {
|
||||
/*The decompressed data is the original image data.*/
|
||||
dsc->img_data = decoder_data->decompressed;
|
||||
res = LV_RESULT_OK;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -670,7 +786,7 @@ static lv_result_t decode_indexed_line(lv_color_format_t color_format, const lv_
|
||||
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)
|
||||
static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, void * buff, uint32_t btr, uint32_t * br)
|
||||
{
|
||||
lv_fs_res_t res;
|
||||
if(br) *br = 0;
|
||||
@ -687,3 +803,50 @@ static lv_fs_res_t fs_read_file_at(lv_fs_file_t * f, uint32_t pos, uint8_t * buf
|
||||
|
||||
return LV_FS_RES_OK;
|
||||
}
|
||||
|
||||
static lv_result_t decompress_image(lv_image_decoder_dsc_t * dsc, const lv_image_compressed_t * compressed)
|
||||
{
|
||||
/*Need to store decompressed data to decoder to free on close*/
|
||||
decoder_data_t * decoder_data = get_decoder_data(dsc);
|
||||
if(decoder_data == NULL) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
uint8_t * decompressed;
|
||||
uint32_t input_len = compressed->compressed_size;
|
||||
uint32_t out_len = compressed->decompressed_size;
|
||||
|
||||
/*Note, stride must match.*/
|
||||
decompressed = lv_draw_buf_malloc(out_len, dsc->header.cf);
|
||||
if(decompressed == NULL) {
|
||||
LV_LOG_WARN("No memory for decompressed image, input: %" LV_PRIu32 ", output: %" LV_PRIu32, input_len, out_len);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
if(compressed->method == LV_IMAGE_COMPRESS_RLE) {
|
||||
#if LV_USE_RLE
|
||||
/*Compress always happen on byte*/
|
||||
uint32_t pixel_byte;
|
||||
if(dsc->header.cf == LV_COLOR_FORMAT_RGB565A8)
|
||||
pixel_byte = 2;
|
||||
else
|
||||
pixel_byte = (lv_color_format_get_bpp(dsc->header.cf) + 7) >> 3;
|
||||
const uint8_t * input = compressed->data;
|
||||
uint8_t * output = decompressed;
|
||||
uint32_t len;
|
||||
len = lv_rle_decompress(input, input_len, output, out_len, pixel_byte);
|
||||
if(len != compressed->decompressed_size) {
|
||||
LV_LOG_WARN("Decompress failed: %" LV_PRIu32 ", got: %" LV_PRIu32, out_len, len);
|
||||
lv_draw_buf_free(decompressed);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
#else
|
||||
LV_LOG_WARN("RLE decompress is not enabled");
|
||||
lv_draw_buf_free(decompressed);
|
||||
return LV_RESULT_INVALID;
|
||||
#endif
|
||||
}
|
||||
|
||||
decoder_data->decompressed = decompressed; /*Free on decoder close*/
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
99
src/libs/rle/lv_rle.c
Normal file
99
src/libs/rle/lv_rle.c
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @file lv_rle.c
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../stdlib/lv_string.h"
|
||||
#include "lv_rle.h"
|
||||
|
||||
#if LV_USE_RLE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
uint32_t lv_rle_decompress(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;
|
||||
|
||||
lv_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. */
|
||||
lv_memset(output, input[0], ctrl_byte);
|
||||
output += ctrl_byte;
|
||||
}
|
||||
else {
|
||||
for(uint32_t i = 0; i < ctrl_byte; i++) {
|
||||
lv_memcpy(output, input, blk_size);
|
||||
output += blk_size;
|
||||
}
|
||||
}
|
||||
input += blk_size;
|
||||
}
|
||||
}
|
||||
|
||||
return wr_len;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_RLE*/
|
46
src/libs/rle/lv_rle.h
Normal file
46
src/libs/rle/lv_rle.h
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file lv_rle.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_RLE_H
|
||||
#define LV_RLE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../../lvgl.h"
|
||||
|
||||
#if LV_USE_RLE
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
uint32_t lv_rle_decompress(const uint8_t * input,
|
||||
uint32_t input_buff_len, uint8_t * output,
|
||||
uint32_t output_buff_len, uint8_t blk_size);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_RLE*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*LV_RLE_H*/
|
BIN
tests/ref_imgs/draw/image_format_rle_compressed.png
Normal file
BIN
tests/ref_imgs/draw/image_format_rle_compressed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
tests/ref_imgs/draw/image_format_rle_compressed_rotate.png
Normal file
BIN
tests/ref_imgs/draw/image_format_rle_compressed_rotate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 122 KiB |
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
@ -159,6 +159,7 @@ void test_image_built_in_decode_rotate_and_recolor(void)
|
||||
|
||||
void test_image_rle_decode(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/binimages/cogwheel.A1.rle", false, false);
|
||||
img_create("rleA2", "A:src/test_files/binimages/cogwheel.A2.rle", false, false);
|
||||
img_create("rleA4", "A:src/test_files/binimages/cogwheel.A4.rle", false, false);
|
||||
@ -174,10 +175,12 @@ void test_image_rle_decode(void)
|
||||
img_create("rleARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.rle", false, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle.png");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_image_rle_decode_rotate(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/binimages/cogwheel.A1.rle", true, false);
|
||||
img_create("rleA2", "A:src/test_files/binimages/cogwheel.A2.rle", true, false);
|
||||
img_create("rleA4", "A:src/test_files/binimages/cogwheel.A4.rle", true, false);
|
||||
@ -193,10 +196,12 @@ void test_image_rle_decode_rotate(void)
|
||||
img_create("rleARGB8888", "A:src/test_files/binimages/cogwheel.ARGB8888.rle", true, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_rotate.png");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_image_rle_decode_rotate_recolor(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/binimages/cogwheel.A1.rle", true, true);
|
||||
img_create("rleA2", "A:src/test_files/binimages/cogwheel.A2.rle", true, true);
|
||||
img_create("rleA4", "A:src/test_files/binimages/cogwheel.A4.rle", true, true);
|
||||
@ -212,6 +217,70 @@ void test_image_rle_decode_rotate_recolor(void)
|
||||
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
|
||||
}
|
||||
|
||||
void test_image_rle_compressed_decode(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/rle_compressed/cogwheel.A1.bin", false, false);
|
||||
img_create("rleA2", "A:src/test_files/rle_compressed/cogwheel.A2.bin", false, false);
|
||||
img_create("rleA4", "A:src/test_files/rle_compressed/cogwheel.A4.bin", false, false);
|
||||
img_create("rleA8", "A:src/test_files/rle_compressed/cogwheel.A8.bin", false, false);
|
||||
img_create("rleI1", "A:src/test_files/rle_compressed/cogwheel.I1.bin", false, false);
|
||||
img_create("rleI2", "A:src/test_files/rle_compressed/cogwheel.I2.bin", false, false);
|
||||
img_create("rleI4", "A:src/test_files/rle_compressed/cogwheel.I4.bin", false, false);
|
||||
img_create("rleI8", "A:src/test_files/rle_compressed/cogwheel.I8.bin", false, false);
|
||||
img_create("rleRGB565A8", "A:src/test_files/rle_compressed/cogwheel.RGB565A8.bin", false, false);
|
||||
img_create("rleRGB565", "A:src/test_files/rle_compressed/cogwheel.RGB565.bin", false, false);
|
||||
img_create("rleRGB888", "A:src/test_files/rle_compressed/cogwheel.RGB888.bin", false, false);
|
||||
img_create("rleXRGB8888", "A:src/test_files/rle_compressed/cogwheel.XRGB8888.bin", false, false);
|
||||
img_create("rleARGB8888", "A:src/test_files/rle_compressed/cogwheel.ARGB8888.bin", false, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_compressed.png");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_image_rle_compressed_decode_rotate(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/rle_compressed/cogwheel.A1.bin", true, false);
|
||||
img_create("rleA2", "A:src/test_files/rle_compressed/cogwheel.A2.bin", true, false);
|
||||
img_create("rleA4", "A:src/test_files/rle_compressed/cogwheel.A4.bin", true, false);
|
||||
img_create("rleA8", "A:src/test_files/rle_compressed/cogwheel.A8.bin", true, false);
|
||||
img_create("rleI1", "A:src/test_files/rle_compressed/cogwheel.I1.bin", true, false);
|
||||
img_create("rleI2", "A:src/test_files/rle_compressed/cogwheel.I2.bin", true, false);
|
||||
img_create("rleI4", "A:src/test_files/rle_compressed/cogwheel.I4.bin", true, false);
|
||||
img_create("rleI8", "A:src/test_files/rle_compressed/cogwheel.I8.bin", true, false);
|
||||
img_create("rleRGB565A8", "A:src/test_files/rle_compressed/cogwheel.RGB565A8.bin", true, false);
|
||||
img_create("rleRGB565", "A:src/test_files/rle_compressed/cogwheel.RGB565.bin", true, false);
|
||||
img_create("rleRGB888", "A:src/test_files/rle_compressed/cogwheel.RGB888.bin", true, false);
|
||||
img_create("rleXRGB8888", "A:src/test_files/rle_compressed/cogwheel.XRGB8888.bin", true, false);
|
||||
img_create("rleARGB8888", "A:src/test_files/rle_compressed/cogwheel.ARGB8888.bin", true, false);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_compressed_rotate.png");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_image_rle_compressed_decode_rotate_recolor(void)
|
||||
{
|
||||
#if LV_USE_RLE
|
||||
img_create("rleA1", "A:src/test_files/rle_compressed/cogwheel.A1.bin", true, true);
|
||||
img_create("rleA2", "A:src/test_files/rle_compressed/cogwheel.A2.bin", true, true);
|
||||
img_create("rleA4", "A:src/test_files/rle_compressed/cogwheel.A4.bin", true, true);
|
||||
img_create("rleA8", "A:src/test_files/rle_compressed/cogwheel.A8.bin", true, true);
|
||||
img_create("rleI1", "A:src/test_files/rle_compressed/cogwheel.I1.bin", true, true);
|
||||
img_create("rleI2", "A:src/test_files/rle_compressed/cogwheel.I2.bin", true, true);
|
||||
img_create("rleI4", "A:src/test_files/rle_compressed/cogwheel.I4.bin", true, true);
|
||||
img_create("rleI8", "A:src/test_files/rle_compressed/cogwheel.I8.bin", true, true);
|
||||
img_create("rleRGB565A8", "A:src/test_files/rle_compressed/cogwheel.RGB565A8.bin", true, true);
|
||||
img_create("rleRGB565", "A:src/test_files/rle_compressed/cogwheel.RGB565.bin", true, true);
|
||||
img_create("rleRGB888", "A:src/test_files/rle_compressed/cogwheel.RGB888.bin", true, true);
|
||||
img_create("rleXRGB8888", "A:src/test_files/rle_compressed/cogwheel.XRGB8888.bin", true, true);
|
||||
img_create("rleARGB8888", "A:src/test_files/rle_compressed/cogwheel.ARGB8888.bin", true, true);
|
||||
|
||||
TEST_ASSERT_EQUAL_SCREENSHOT("draw/image_format_rle_compressed_rotate_recolor.png");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
BIN
tests/src/test_files/rle_compressed/cogwheel.A1.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.A1.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.A2.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.A2.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.A4.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.A4.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.A8.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.A8.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.ARGB8888.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.ARGB8888.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.I1.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.I1.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.I2.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.I2.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.I4.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.I4.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.I8.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.I8.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB565.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB565.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB565A8.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB565A8.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB888.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.RGB888.bin
Normal file
Binary file not shown.
BIN
tests/src/test_files/rle_compressed/cogwheel.XRGB8888.bin
Normal file
BIN
tests/src/test_files/rle_compressed/cogwheel.XRGB8888.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user