1
0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2024-11-26 13:25:54 +00:00

COMPACT_CUSTOM_BOOTSCREEN (#26419)

This commit is contained in:
Scott Lahteine 2023-11-13 17:24:15 -06:00 committed by GitHub
parent c74e6ad868
commit dc26531207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 222 additions and 6 deletions

View File

@ -127,14 +127,47 @@ bool MarlinUI::detected() { return true; }
#else #else
const u8g_pgm_uint8_t * const bmp = (u8g_pgm_uint8_t*)pgm_read_ptr(&custom_bootscreen_animation[frame]); const u8g_pgm_uint8_t * const bmp = (u8g_pgm_uint8_t*)pgm_read_ptr(&custom_bootscreen_animation[frame]);
#endif #endif
#elif ENABLED(COMPACT_CUSTOM_BOOTSCREEN)
#define BMPSIZE (CUSTOM_BOOTSCREEN_BMP_BYTEWIDTH * CUSTOM_BOOTSCREEN_BMPHEIGHT)
uint8_t bmp[BMPSIZE];
uint8_t *bmp_rle = (uint8_t*)custom_start_bmp_rle;
#else #else
const u8g_pgm_uint8_t * const bmp = custom_start_bmp; const u8g_pgm_uint8_t * const bmp = custom_start_bmp;
#endif #endif
#if ENABLED(COMPACT_CUSTOM_BOOTSCREEN)
uint8_t *dst = (uint8_t*)bmp;
auto rle_nybble = [&](const uint16_t i) {
const uint8_t b = bmp_rle[i / 2];
return (i & 1 ? b & 0xF : b >> 4);
};
uint8_t workbyte = 0, bitstate = rle_nybble(0) << 7;
uint16_t inindex = 1, outindex = 0;
while (outindex < BMPSIZE * 8) {
int16_t c = rle_nybble(inindex++);
if (c == 15) {
c = 16 * rle_nybble(inindex) + rle_nybble(inindex + 1) + 15; // From 16 to 270
inindex += 2;
}
while (c-- >= 0) {
const uint8_t bitind = outindex & 7,
bitval = bitstate >> bitind;
workbyte |= bitval;
if (bitind == 7) { *dst++ = workbyte; workbyte = 0; }
outindex++;
}
bitstate ^= 0x80;
}
#endif // COMPACT_CUSTOM_BOOTSCREEN
u8g.TERN(COMPACT_CUSTOM_BOOTSCREEN, drawBitmap, drawBitmapP)
(left, top, CUSTOM_BOOTSCREEN_BMP_BYTEWIDTH, CUSTOM_BOOTSCREEN_BMPHEIGHT, bmp);
UNUSED(frame); UNUSED(frame);
u8g.drawBitmapP(left, top, CUSTOM_BOOTSCREEN_BMP_BYTEWIDTH, CUSTOM_BOOTSCREEN_BMPHEIGHT, bmp);
#if ENABLED(CUSTOM_BOOTSCREEN_INVERTED) #if ENABLED(CUSTOM_BOOTSCREEN_INVERTED)
if (frame == 0) { if (frame == 0) {
u8g.setColorIndex(1); u8g.setColorIndex(1);

View File

@ -67,10 +67,10 @@ def addCompressedData(input_file, output_file):
i += 1 i += 1
rsize = 1 rsize = 1
for j in range(i, len(data)): for j in range(i, len(data)):
if v != data[j]: break; if v != data[j]: break
i += 1 i += 1
rsize += 1 rsize += 1
if rsize >= 128: break; if rsize >= 128: break
# If the run is one, add to the distinct values # If the run is one, add to the distinct values
if rsize == 1: distinct.append(v) if rsize == 1: distinct.append(v)
@ -131,7 +131,7 @@ def addCompressedData(input_file, output_file):
if len(sys.argv) <= 2: if len(sys.argv) <= 2:
print("Utility to compress Marlin RGB565 TFT data to RLE16 format.") print("Utility to compress Marlin RGB565 TFT data to RLE16 format.")
print("Reads the existing Marlin RGB565 cpp file and generates a new file with the additional RLE16 data.") 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") print("Usage: rle16_compress_cpp_image_data.py INPUT_FILE.cpp OUTPUT_FILE.cpp")
exit(1) exit(1)

View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
#
# Bitwise RLE compress a Marlin mono DOGM bitmap.
# Input: An existing Marlin Marlin mono DOGM bitmap .cpp or .h file.
# Output: A new file with the original and compressed data.
#
# Usage: rle_compress_bitmap.py INPUT_FILE OUTPUT_FILE
#
import sys,struct
import re
def addCompressedData(input_file, output_file):
ofile = open(output_file, 'wt')
datatype = "uint8_t"
bytewidth = 16
raw_data = []
arrname = ''
c_data_section = False ; c_skip_data = False ; c_footer = False
while True:
line = input_file.readline()
if not line: break
if not c_footer:
if not c_skip_data: ofile.write(line)
mat = re.match(r'.+CUSTOM_BOOTSCREEN_BMPWIDTH\s+(\d+)', line)
if mat: bytewidth = (int(mat[1]) + 7) // 8
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)
mat = re.match(r'(0b|B)[01]{8}', cleaned)
if mat:
as_list = cleaned.split(mat[1])
as_list.pop(0)
raw_data += [int(x, 2) for x in as_list]
else:
as_list = cleaned.split("0x")
as_list.pop(0)
raw_data += [int(x, 16) for x in as_list]
mat = re.match(r'const (uint\d+_t|unsigned char)', line)
if mat:
# e.g.: const unsigned char custom_start_bmp[] PROGMEM = {
datatype = mat[0]
if "_rle" in line:
c_skip_data = True
else:
c_data_section = True
arrname = line.split('[')[0].split(' ')[-1]
print("Found data array", arrname)
input_file.close()
#print("\nRaw Bitmap Data", raw_data)
#
# Bitwise RLE (run length) encoding
# Convert data from raw mono bitmap to a bitwise run-length-encoded format.
# - The first nybble is the starting bit state. Changing this nybble inverts the bitmap.
# - The following bytes provide the runs for alternating on/off bits.
# - A value of 0-14 encodes a run of 1-15.
# - A value of 16 indicates a run of 16-270 calculated using the next two bytes.
#
def bitwise_rle_encode(data):
warn = "This may take a while" if len(data) > 300000 else ""
print("Compressing image data...", warn)
def get_bit(data, n): return 1 if (data[n // 8] & (0x80 >> (n & 7))) else 0
bitslen = len(data) * 8
bitstate = get_bit(data, 0)
rledata = [ bitstate ]
i = 0
runlen = -1
while i <= bitslen:
if i < bitslen: b = get_bit(data, i)
runlen += 1
if bitstate != b or i == bitslen:
if i > 11 * 56 * 8: print(f'Bit change at index {i} with runlen={runlen}')
if runlen >= 16:
rledata += [ 15, runlen // 16 - 1, runlen % 16 ]
if i > 11 * 56 * 8: print(f'Storing {[ 15, runlen // 16 - 1, runlen % 16 ]}')
else:
rledata += [ runlen - 1 ]
if i > 11 * 56 * 8: print(f'Storing {[ runlen ]}')
bitstate ^= 1
runlen = 0
i += 1
print("\nrledata", rledata)
encoded = []
ri = 0
rlen = len(rledata)
while ri < rlen:
v = rledata[ri] << 4
if (ri < rlen - 1): v |= rledata[ri + 1]
encoded += [ v ]
ri += 2
print("\nencoded", encoded)
return encoded
def bitwise_rle_decode(rledata, invert=0):
expanded = []
for n in rledata: expanded += [ n >> 4, n & 0xF ]
decoded = []
bitstate = 0 ; workbyte = 0 ; outindex = 0
i = 0
while i < len(expanded):
c = expanded[i]
i += 1
if i == 1: bitstate = c ; continue
if c == 15:
c = 16 * expanded[i] + expanded[i + 1] + 15
i += 2
for _ in range(c, -1, -1):
bitval = 0x80 >> (outindex & 7)
if bitstate: workbyte |= bitval
if bitval == 1:
decoded += [ workbyte ]
workbyte = 0
outindex += 1
bitstate ^= 1
print("\nDecoded RLE data:")
pretty = [ '{0:08b}'.format(v) for v in decoded ]
rows = [pretty[i:i+bytewidth] for i in range(0, len(pretty), bytewidth)]
for row in rows: print(f"{''.join(row)}")
return decoded
def rle_emit(ofile, arrname, rledata, rawsize):
outstr = ''
rows = [ rledata[i:i+16] for i in range(0, len(rledata), 16) ]
for i in range(0, len(rows)):
rows[i] = [ '0x{0:02X}'.format(v) for v in rows[i] ]
outstr += f" {', '.join(rows[i])},\n"
outstr = outstr[:-2]
size = len(rledata)
ofile.write("\n// Saves %i bytes\n%s %s_rle[%d] PROGMEM = {\n%s\n};\n" % (rawsize - size, datatype, arrname, size, outstr))
# Encode the data, write it out, close the file
rledata = bitwise_rle_encode(raw_data)
rle_emit(ofile, arrname, rledata, len(raw_data))
ofile.close()
# Validate that code properly compressed (and decompressed) the data
checkdata = bitwise_rle_decode(rledata)
badindex = -1
for i in range(0, len(checkdata)):
if raw_data[i] != checkdata[i]:
badindex = i
break
if badindex >= 0: print(f'Data mismatch at byte {badindex}')
if len(sys.argv) <= 2:
print('Usage: rle_compress_bitmap.py INPUT_FILE OUTPUT_FILE')
exit(1)
output_cpp = sys.argv[2]
inname = sys.argv[1].replace('//', '/')
try:
input_cpp = open(inname)
print("Processing", inname, "...")
addCompressedData(input_cpp, output_cpp)
except OSError:
print("Can't find input file", inname)