Files
pi-dashboard/pi/image_handler.py

53 lines
1.7 KiB
Python
Raw Permalink Normal View History

2026-02-15 22:14:06 +09:00
"""Status image functions — loading, alpha compositing, and WS transmission."""
import json
import logging
from pathlib import Path
from PIL import Image
log = logging.getLogger(__name__)
IMG_DIR = Path(__file__).parent / "assets" / "img"
2026-02-16 14:52:20 +09:00
STATUS_IMG_SIZE = 200
2026-02-15 22:24:29 +09:00
MONOCHROME_THRESHOLD = 180
2026-02-15 22:14:06 +09:00
def load_status_image(path: Path) -> bytes:
2026-02-16 14:52:20 +09:00
"""Load a PNG, convert to 1-bit 200x200 monochrome bitmap (MSB-first, black=1).
2026-02-15 22:14:06 +09:00
Transparent pixels are composited onto white so they don't render as black.
"""
img = Image.open(path)
# Composite transparent pixels onto white background
if img.mode in ("RGBA", "LA", "PA"):
bg = Image.new("RGBA", img.size, (255, 255, 255, 255))
bg.paste(img, mask=img.split()[-1])
img = bg
img = img.convert("L")
# Resize to fit within 120x120, preserving aspect ratio
img.thumbnail((STATUS_IMG_SIZE, STATUS_IMG_SIZE), Image.LANCZOS)
# Paste centered onto white canvas
canvas = Image.new("L", (STATUS_IMG_SIZE, STATUS_IMG_SIZE), 255)
x_off = (STATUS_IMG_SIZE - img.width) // 2
y_off = (STATUS_IMG_SIZE - img.height) // 2
canvas.paste(img, (x_off, y_off))
2026-02-15 22:23:28 +09:00
# Threshold to 1-bit: black (< MONOCHROME_THRESHOLD) -> 1, white -> 0
2026-02-15 22:14:06 +09:00
bw = canvas.point(lambda p: 1 if p < MONOCHROME_THRESHOLD else 0, "1")
raw = bw.tobytes()
log.info("Status image loaded: %s -> %d bytes", path.name, len(raw))
return raw
async def send_status_image(ws, img_bytes: bytes):
"""Send a status image over the WebSocket (text header + binary payload)."""
header = json.dumps({"type": "status_image", "width": STATUS_IMG_SIZE, "height": STATUS_IMG_SIZE})
await ws.send(header)
await ws.send(img_bytes)
log.info("Sent status image (%d bytes)", len(img_bytes))