feat(tool): add compressed binary image support to C-array (#4924)

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
This commit is contained in:
Neo Xu
2023-12-05 23:28:48 +08:00
committed by GitHub
parent c902556794
commit b655486cc2

View File

@@ -13,7 +13,6 @@ try:
except ImportError: except ImportError:
raise ImportError("Need pypng package, do `pip3 install pypng`") raise ImportError("Need pypng package, do `pip3 install pypng`")
try: try:
import lz4.block import lz4.block
except ImportError: except ImportError:
@@ -395,17 +394,12 @@ class LVGLImage:
h: int = 0, h: int = 0,
data: bytes = b'') -> None: data: bytes = b'') -> None:
self.stride = 0 # default no valid stride value self.stride = 0 # default no valid stride value
self.compress = CompressMethod.NONE
self.set_data(cf, w, h, data) self.set_data(cf, w, h, data)
def __repr__(self) -> str: def __repr__(self) -> str:
return (f"'LVGL image {self.w}x{self.h}, {self.cf.name}," return (f"'LVGL image {self.w}x{self.h}, {self.cf.name},"
f" (12+{self.data_len})Byte'") 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): def adjust_stride(self, stride: int = 0, align: int = 1):
''' '''
Stride can be set directly, or by stride alignment in bytes Stride can be set directly, or by stride alignment in bytes
@@ -486,26 +480,6 @@ class LVGLImage:
p += a8_stride * self.h if self.cf == ColorFormat.RGB565A8 else 0 p += a8_stride * self.h if self.cf == ColorFormat.RGB565A8 else 0
return p return p
@property
def binary(self) -> bytearray:
'''
Return binary of this image including correct lvgl image header and
raw data
'''
bin = bytearray()
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
compress = LVGLCompressData(self.cf, self.compress, self.data)
bin += compress.compressed
return bin
@property @property
def header(self) -> bytearray: def header(self) -> bytearray:
return LVGLImageHeader(self.cf, self.w, self.h) return LVGLImageHeader(self.cf, self.w, self.h)
@@ -570,7 +544,9 @@ class LVGLImage:
logging.info(f"mkdir of {dir} for {filename}") logging.info(f"mkdir of {dir} for {filename}")
os.makedirs(dir) os.makedirs(dir)
def to_bin(self, filename: str): def to_bin(self,
filename: str,
compress: CompressMethod = CompressMethod.NONE):
''' '''
Write this image to file, filename should be ended with '.bin' Write this image to file, filename should be ended with '.bin'
''' '''
@@ -578,19 +554,38 @@ class LVGLImage:
self._check_dir(filename) self._check_dir(filename)
with open(filename, "wb+") as f: with open(filename, "wb+") as f:
f.write(self.binary) bin = bytearray()
flags = 0
flags |= 0x08 if compress != CompressMethod.NONE else 0
header = LVGLImageHeader(self.cf,
self.w,
self.h,
self.stride,
flags=flags)
bin += header.binary
compressed = LVGLCompressData(self.cf, compress, self.data)
bin += compressed.compressed
f.write(bin)
return self return self
def to_c_array(self, filename: str): def to_c_array(self,
filename: str,
compress: CompressMethod = CompressMethod.NONE):
self._check_ext(filename, ".c") self._check_ext(filename, ".c")
self._check_dir(filename) self._check_dir(filename)
varname = path.basename(filename).split('.')[0] varname = path.basename(filename).split('.')[0]
varname = varname.replace("-", "_") varname = varname.replace("-", "_")
varname += f"_{self.cf.name.lower()}" varname = varname.replace(".", "_")
if self.stride != (self.w * self.cf.bpp + 7) // 8:
varname += f"_stride{self.stride}" flags = "0"
if compress is not CompressMethod.NONE:
flags += " | LV_IMAGE_FLAGS_COMPRESSED"
compressed = LVGLCompressData(self.cf, compress, self.data)
header = f''' header = f'''
#if defined(LV_LVGL_H_INCLUDE_SIMPLE) #if defined(LV_LVGL_H_INCLUDE_SIMPLE)
@@ -608,7 +603,8 @@ class LVGLImage:
#define LV_ATTRIBUTE_IMG_DUST #define LV_ATTRIBUTE_IMG_DUST
#endif #endif
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DUST static const
LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DUST
uint8_t {varname}_map[] = {{ uint8_t {varname}_map[] = {{
''' '''
@@ -617,10 +613,11 @@ uint8_t {varname}_map[] = {{
const lv_img_dsc_t {varname} = {{ const lv_img_dsc_t {varname} = {{
.header.cf = LV_COLOR_FORMAT_{self.cf.name}, .header.cf = LV_COLOR_FORMAT_{self.cf.name},
.header.flags = {flags},
.header.w = {self.w}, .header.w = {self.w},
.header.h = {self.h}, .header.h = {self.h},
.header.stride = {self.stride}, .header.stride = {self.stride},
.data_size = {len(self.data)}, .data_size = {len(compressed.compressed)},
.data = {varname}_map, .data = {varname}_map,
}}; }};
@@ -636,12 +633,16 @@ const lv_img_dsc_t {varname} = {{
with open(filename, "w+") as f: with open(filename, "w+") as f:
f.write(header) f.write(header)
# write palette separately if compress is not CompressMethod.NONE:
ncolors = self.cf.ncolors write_binary(f, compressed.compressed, 16)
if ncolors: else:
write_binary(f, self.data[:ncolors * 4], 16) # write palette separately
ncolors = self.cf.ncolors
if ncolors:
write_binary(f, self.data[:ncolors * 4], 16)
write_binary(f, self.data[ncolors * 4:], self.stride)
write_binary(f, self.data[ncolors * 4:], self.stride)
f.write(ending) f.write(ending)
return self return self
@@ -1032,13 +1033,11 @@ class PNGConverter:
def convert(self): def convert(self):
output = [] output = []
for f in self.files: for f in self.files:
if self.ofmt == OutputFormat.RLE_FILE: if self.ofmt == OutputFormat.RLE_FILE:
rle = RLEImage().from_png(f, rle = RLEImage().from_png(f,
self.cf, self.cf,
background=self.background) background=self.background)
rle.adjust_stride(align=self.align) rle.adjust_stride(align=self.align)
rle.set_compress(self.compress)
output.append((f, rle)) output.append((f, rle))
rle.to_rle(self._replace_ext(f, ".rle")) rle.to_rle(self._replace_ext(f, ".rle"))
else: else:
@@ -1046,12 +1045,13 @@ class PNGConverter:
self.cf, self.cf,
background=self.background) background=self.background)
img.adjust_stride(align=self.align) img.adjust_stride(align=self.align)
img.set_compress(self.compress)
output.append((f, img)) output.append((f, img))
if self.ofmt == OutputFormat.BIN_FILE: if self.ofmt == OutputFormat.BIN_FILE:
img.to_bin(self._replace_ext(f, ".bin")) img.to_bin(self._replace_ext(f, ".bin"),
compress=self.compress)
elif self.ofmt == OutputFormat.C_ARRAY: elif self.ofmt == OutputFormat.C_ARRAY:
img.to_c_array(self._replace_ext(f, ".c")) img.to_c_array(self._replace_ext(f, ".c"),
compress=self.compress)
elif self.ofmt == OutputFormat.PNG_FILE: elif self.ofmt == OutputFormat.PNG_FILE:
img.to_png(self._replace_ext(f, ".png")) img.to_png(self._replace_ext(f, ".png"))
@@ -1142,8 +1142,8 @@ def test():
f = "pngs/cogwheel.RGB565A8.png" f = "pngs/cogwheel.RGB565A8.png"
img = LVGLImage().from_png(f, cf=ColorFormat.RGB888, background=0xFF_FF_00) img = LVGLImage().from_png(f, cf=ColorFormat.RGB888, background=0xFF_FF_00)
img.adjust_stride(align=16) img.adjust_stride(align=16)
img.set_compress(CompressMethod.RLE)
img.to_bin("output/cogwheel.RGB888.bin") img.to_bin("output/cogwheel.RGB888.bin")
img.to_c_array("output/cogwheel-abc.c") # file name is used as c var name
img.to_png("output/cogwheel.RGB888.png.png") # convert back to png img.to_png("output/cogwheel.RGB888.png.png") # convert back to png