Marlin/buildroot/share/scripts/rle16_compress_cpp_image_data.py
2024-10-25 21:01:00 +02:00

143 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Utility to compress Marlin RGB565 TFT data to RLE16 format.
# Reads the existing Marlin RGB565 cpp file and generates a new file with the additional RLE16 data.
#
# Usage: rle16_compress_cpp_image_data.py INPUT_FILE.cpp OUTPUT_FILE.cpp
#
import sys,struct
import re
def addCompressedData(input_file, output_file):
ofile = open(output_file, 'wt')
c_data_section = False
c_skip_data = False
c_footer = False
raw_data = []
rle_value = []
rle_count = []
arrname = ''
line = input_file.readline()
while line:
if not c_footer:
if not c_skip_data: ofile.write(line)
if "};" in line:
c_skip_data = False
c_data_section = False
c_footer = True
if c_data_section:
cleaned = re.sub(r"\s|,|\n", "", line)
as_list = cleaned.split("0x")
as_list.pop(0)
raw_data += [int(x, 16) for x in as_list]
if "const uint" in line:
# e.g.: const uint16_t marlin_logo_480x320x16[153600] = {
if "_rle16" in line:
c_skip_data = True
else:
c_data_section = True
arrname = line.split('[')[0].split(' ')[-1]
print("Found data array", arrname)
line = input_file.readline()
input_file.close()
#
# RLE16 (run length 16) encoding
# Convert data from from raw RGB565 to a simple run-length-encoded format for each word of data.
# - Each sequence begins with a count byte N.
# - If the high bit is set in N the run contains N & 0x7F + 1 unique words.
# - Otherwise it repeats the following word N + 1 times.
# - Each RGB565 word is stored in MSB / LSB order.
#
def rle_encode(data):
warn = "This may take a while" if len(data) > 300000 else ""
print("Compressing image data...", warn)
rledata = []
distinct = []
i = 0
while i < len(data):
v = data[i]
i += 1
rsize = 1
for j in range(i, len(data)):
if v != data[j]: break
i += 1
rsize += 1
if rsize >= 128: break
# If the run is one, add to the distinct values
if rsize == 1: distinct.append(v)
# If distinct length >= 127, or the repeat run is 2 or more,
# store the distinct run.
nr = len(distinct)
if nr and (nr >= 128 or rsize > 1 or i >= len(data)):
rledata += [(nr - 1) | 0x80] + distinct
distinct = []
# If the repeat run is 2 or more, store the repeat run.
if rsize > 1: rledata += [rsize - 1, v]
return rledata
def append_byte(data, byte, cols=240):
if data == '': data = ' '
data += ('0x{0:02X}, '.format(byte)) # 6 characters
if len(data) % (cols * 6 + 2) == 0: data = data.rstrip() + "\n "
return data
def rle_emit(ofile, arrname, rledata, rawsize):
col = 0
i = 0
outstr = ''
size = 0
while i < len(rledata):
rval = rledata[i]
i += 1
if rval & 0x80:
count = (rval & 0x7F) + 1
outstr = append_byte(outstr, rval)
size += 1
for j in range(count):
outstr = append_byte(outstr, rledata[i + j] >> 8)
outstr = append_byte(outstr, rledata[i + j] & 0xFF)
size += 2
i += count
else:
outstr = append_byte(outstr, rval)
outstr = append_byte(outstr, rledata[i] >> 8)
outstr = append_byte(outstr, rledata[i] & 0xFF)
i += 1
size += 3
outstr = outstr.rstrip()[:-1]
ofile.write("\n// Saves %i bytes\nconst uint8_t %s_rle16[%d] = {\n%s\n};\n" % (rawsize - size, arrname, size, outstr))
(w, h, d) = arrname.split("_")[-1].split('x')
ofile.write("\nconst tImage MarlinLogo{0}x{1}x16 = MARLIN_LOGO_CHOSEN({0}, {1});\n".format(w, h))
ofile.write("\n#endif // HAS_GRAPHICAL_TFT && SHOW_BOOTSCREEN\n".format(w, h))
# Encode the data, write it out, close the file
rledata = rle_encode(raw_data)
rle_emit(ofile, arrname, rledata, len(raw_data) * 2)
ofile.close()
if len(sys.argv) <= 2:
print("Utility to compress Marlin RGB565 TFT data to RLE16 format.")
print("Reads a Marlin RGB565 cpp file and generates a new file with the additional RLE16 data.")
print("Usage: rle16_compress_cpp_image_data.py INPUT_FILE.cpp OUTPUT_FILE.cpp")
exit(1)
output_cpp = sys.argv[2]
inname = sys.argv[1].replace('//', '/')
input_cpp = open(inname)
print("Processing", inname, "...")
addCompressedData(input_cpp, output_cpp)