#!/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" ) ) 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("