#!/usr/bin/env python3 """Convert Doc/UI 0831 trial PNG slices to RGB565 C arrays for mode-select.""" from PIL import Image, ImageDraw from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "tools" / "assets_0831" OUT_C = ROOT / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ModeSelect0831.c" OUT_H = ROOT / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ModeSelect0831.h" PREV = SRC / "preview_mode_select.png" # Only emit composite rows (icon+MiSans text) to save Flash. ROW_W, ROW_H = 180, 52 NAMES = [ ("Universal", "mode_row_0.png"), # 万能 ("Normal", "mode_row_1.png"), # 普通 ("Expert", "mode_row_2.png"), # 专业 ] def rgba_to_rgb565_bytes(im: Image.Image) -> bytes: im = im.convert("RGBA") px = im.load() w, h = im.size out = bytearray(w * h * 2) i = 0 for y in range(h): for x in range(w): r, g, b, a = px[x, y] if a < 40 or (r < 12 and g < 12 and b < 12): out[i] = 0 out[i + 1] = 0 else: r = (r * a) // 255 g = (g * a) // 255 b = (b * a) // 255 c = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3) out[i] = (c >> 8) & 0xFF out[i + 1] = c & 0xFF i += 2 return bytes(out) def fit_on_canvas(src_im: Image.Image, cw: int, ch: int) -> Image.Image: im = src_im.convert("RGBA") bbox = im.getbbox() if bbox: im = im.crop(bbox) sw, sh = im.size scale = min(cw / sw, ch / sh) nw = max(1, int(round(sw * scale))) nh = max(1, int(round(sh * scale))) im = im.resize((nw, nh), Image.Resampling.LANCZOS) canvas = Image.new("RGBA", (cw, ch), (0, 0, 0, 255)) ox = (cw - nw) // 2 oy = (ch - nh) // 2 canvas.paste(im, (ox, oy), im) return canvas def emit_array(name: str, data: bytes, w: int, h: int, lines: list) -> None: lines.append(f"const unsigned char {name}[{len(data)}] = {{ /* {w}x{h} RGB565 BE */") for i in range(0, len(data), 16): chunk = data[i : i + 16] hexes = ",".join(f"0x{b:02X}" for b in chunk) lines.append(hexes + ",") lines.append("};") lines.append("") def main() -> None: c_lines = [ '#include "Drv_ILI9341_Lcd_Image_ModeSelect0831.h"', "", "/* Auto-generated from Doc/UI 0831 trial PNG. Do not hand-edit. */", "", ] h_lines = [ "#ifndef __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H", "#define __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H", "", '#include "stdint.h"', "", f"#define UI_MODE0831_ROW_W {ROW_W}", f"#define UI_MODE0831_ROW_H {ROW_H}", "", ] screen = Image.new("RGBA", (240, 320), (0, 0, 0, 255)) d = ImageDraw.Draw(screen) d.rectangle([0, 0, 239, 27], fill=(20, 28, 40, 255)) btn_ys = [40, 132, 224] btn_w, btn_h, btn_r = 220, 87, 10 total = 0 for idx, (tag, row_n) in enumerate(NAMES): row = fit_on_canvas(Image.open(SRC / row_n), ROW_W, ROW_H) row.save(SRC / f"gen_{tag.lower()}_row.png") rdata = rgba_to_rgb565_bytes(row) total += len(rdata) rname = f"gImage_Mode0831_{tag}_Row_{ROW_W}_{ROW_H}" emit_array(rname, rdata, ROW_W, ROW_H, c_lines) h_lines.append(f"extern const unsigned char {rname}[];") h_lines.append("") by = btn_ys[idx] bx = (240 - btn_w) // 2 fill = (47, 54, 71, 255) if idx == 0 else (25, 32, 45, 255) d.rounded_rectangle([bx, by, bx + btn_w - 1, by + btn_h - 1], radius=btn_r, fill=fill) rx = bx + (btn_w - ROW_W) // 2 ry = by + (btn_h - ROW_H) // 2 screen.paste(row, (rx, ry), row) h_lines += ["#endif", ""] OUT_H.write_text("\n".join(h_lines), encoding="utf-8") OUT_C.write_text("\n".join(c_lines), encoding="utf-8") screen.convert("RGB").save(PREV) print(f"wrote {OUT_C} ({OUT_C.stat().st_size} bytes)") print(f"wrote {OUT_H}") print(f"flash payload ~{total} bytes") print(f"preview {PREV}") if __name__ == "__main__": main()