feat(launcher): restore custom SD background experience
This commit is contained in:
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Convert an image to an uncompressed LVGL RGB565 launcher background."""
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LV_IMAGE_HEADER_MAGIC = 0x19
|
||||
LV_COLOR_FORMAT_RGB565 = 0x12
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Create an uncompressed LVGL .bin image for "
|
||||
"/sdcard/tactility/launcher/background.bin. Use a square image sized "
|
||||
"to the display's longest edge to support both orientations without scaling "
|
||||
"(for example, 320x320 for a 320x240 display)."
|
||||
)
|
||||
)
|
||||
parser.add_argument("input", type=Path, help="Source image")
|
||||
parser.add_argument("output", type=Path, help="Destination .bin file")
|
||||
parser.add_argument("--width", type=int, required=True, help="Output width")
|
||||
parser.add_argument("--height", type=int, required=True, help="Output height")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
if args.width <= 0 or args.width > 65535 or args.height <= 0 or args.height > 65535:
|
||||
raise SystemExit("width and height must be between 1 and 65535")
|
||||
|
||||
magick = shutil.which("magick")
|
||||
if magick is None:
|
||||
raise SystemExit("ImageMagick is required (the 'magick' command was not found)")
|
||||
|
||||
command = [
|
||||
magick,
|
||||
str(args.input),
|
||||
"-resize",
|
||||
f"{args.width}x{args.height}^",
|
||||
"-gravity",
|
||||
"center",
|
||||
"-extent",
|
||||
f"{args.width}x{args.height}",
|
||||
"-depth",
|
||||
"8",
|
||||
"rgb:-",
|
||||
]
|
||||
rgb888 = subprocess.run(command, check=True, stdout=subprocess.PIPE).stdout
|
||||
expected_size = args.width * args.height * 3
|
||||
if len(rgb888) != expected_size:
|
||||
raise SystemExit(f"unexpected ImageMagick output: {len(rgb888)} bytes, expected {expected_size}")
|
||||
|
||||
rgb565 = bytearray(args.width * args.height * 2)
|
||||
for source_offset in range(0, len(rgb888), 3):
|
||||
r, g, b = rgb888[source_offset : source_offset + 3]
|
||||
pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
|
||||
destination_offset = (source_offset // 3) * 2
|
||||
struct.pack_into("<H", rgb565, destination_offset, pixel)
|
||||
|
||||
stride = args.width * 2
|
||||
header = struct.pack(
|
||||
"<BBHHHHH",
|
||||
LV_IMAGE_HEADER_MAGIC,
|
||||
LV_COLOR_FORMAT_RGB565,
|
||||
0,
|
||||
args.width,
|
||||
args.height,
|
||||
stride,
|
||||
0,
|
||||
)
|
||||
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_bytes(header + rgb565)
|
||||
print(
|
||||
f"Wrote {args.output} ({args.width}x{args.height}, "
|
||||
f"{len(header) + len(rgb565)} bytes, uncompressed RGB565)"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user