134 lines
4.1 KiB
Python
134 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
from PIL import Image, ImageDraw
|
|
|
|
from .text_renderer import TextOptions, render_text
|
|
|
|
DIAGNOSTIC_MODES = {
|
|
"corners_lines",
|
|
"row_bands",
|
|
"address_check",
|
|
"address_bit_0",
|
|
"address_bit_1",
|
|
"address_bit_2",
|
|
"address_bit_3",
|
|
"address_bit_4",
|
|
"text_ok123",
|
|
"clear",
|
|
}
|
|
|
|
|
|
def render_diagnostic(mode: str, size: tuple[int, int] = (64, 64)) -> Image.Image:
|
|
if mode not in DIAGNOSTIC_MODES:
|
|
allowed = ", ".join(sorted(DIAGNOSTIC_MODES))
|
|
raise ValueError(f"diagnostic mode must be one of: {allowed}")
|
|
|
|
if mode == "clear":
|
|
return Image.new("RGB", size, (0, 0, 0))
|
|
if mode == "corners_lines":
|
|
return _render_corners_lines(size)
|
|
if mode == "row_bands":
|
|
return _render_row_bands(size)
|
|
if mode == "address_check":
|
|
return _render_address_check(size)
|
|
if mode.startswith("address_bit_"):
|
|
return _render_address_bit(int(mode.rsplit("_", 1)[1]), size)
|
|
return render_text(TextOptions(text="OK123", size=12, x=4, y=24), size=size)
|
|
|
|
|
|
def _render_corners_lines(size: tuple[int, int]) -> Image.Image:
|
|
width, height = size
|
|
image = Image.new("RGB", size, (0, 0, 0))
|
|
draw = ImageDraw.Draw(image)
|
|
|
|
for y, color in ((0, (80, 80, 80)), (31, (255, 180, 0)), (32, (0, 180, 255)), (63, (80, 80, 80))):
|
|
if y < height:
|
|
draw.line((0, y, width - 1, y), fill=color)
|
|
for x in (0, width - 1):
|
|
draw.line((x, 0, x, height - 1), fill=(80, 80, 80))
|
|
|
|
marker = 4
|
|
draw.rectangle((0, 0, marker, marker), fill=(255, 0, 0))
|
|
draw.rectangle((width - marker - 1, 0, width - 1, marker), fill=(0, 255, 0))
|
|
draw.rectangle((0, height - marker - 1, marker, height - 1), fill=(0, 0, 255))
|
|
draw.rectangle((width - marker - 1, height - marker - 1, width - 1, height - 1), fill=(255, 255, 255))
|
|
return image
|
|
|
|
|
|
def _render_row_bands(size: tuple[int, int]) -> Image.Image:
|
|
width, height = size
|
|
image = Image.new("RGB", size, (0, 0, 0))
|
|
draw = ImageDraw.Draw(image)
|
|
colors = (
|
|
(255, 0, 0),
|
|
(255, 120, 0),
|
|
(255, 255, 0),
|
|
(0, 255, 0),
|
|
(0, 255, 255),
|
|
(0, 0, 255),
|
|
(180, 0, 255),
|
|
(255, 255, 255),
|
|
)
|
|
|
|
band_height = max(1, height // len(colors))
|
|
for index, color in enumerate(colors):
|
|
y0 = index * band_height
|
|
y1 = height - 1 if index == len(colors) - 1 else min(height - 1, y0 + band_height - 1)
|
|
draw.rectangle((0, y0, width - 1, y1), fill=color)
|
|
draw.line((0, y0, width - 1, y0), fill=(0, 0, 0))
|
|
return image
|
|
|
|
|
|
def _render_address_check(size: tuple[int, int]) -> Image.Image:
|
|
width, height = size
|
|
image = Image.new("RGB", size, (0, 0, 0))
|
|
draw = ImageDraw.Draw(image)
|
|
|
|
bit_colors = (
|
|
(255, 0, 0),
|
|
(0, 255, 0),
|
|
(0, 0, 255),
|
|
(255, 255, 0),
|
|
(255, 0, 255),
|
|
)
|
|
segment_width = max(1, width // len(bit_colors))
|
|
|
|
for y in range(height):
|
|
for bit, color in enumerate(bit_colors):
|
|
if y & (1 << bit):
|
|
x0 = bit * segment_width
|
|
x1 = width - 1 if bit == len(bit_colors) - 1 else min(width - 1, x0 + segment_width - 2)
|
|
draw.line((x0, y, x1, y), fill=color)
|
|
|
|
diagonal_x = min(width - 1, y)
|
|
draw.point((diagonal_x, y), fill=(255, 255, 255))
|
|
draw.point((width - 1 - diagonal_x, y), fill=(80, 80, 80))
|
|
|
|
for y in (0, 31, 32, 63):
|
|
if y < height:
|
|
draw.line((0, y, width - 1, y), fill=(255, 255, 255))
|
|
return image
|
|
|
|
|
|
def _render_address_bit(bit: int, size: tuple[int, int]) -> Image.Image:
|
|
width, height = size
|
|
image = Image.new("RGB", size, (0, 0, 0))
|
|
draw = ImageDraw.Draw(image)
|
|
colors = (
|
|
(255, 0, 0),
|
|
(0, 255, 0),
|
|
(0, 0, 255),
|
|
(255, 255, 0),
|
|
(255, 0, 255),
|
|
)
|
|
color = colors[bit]
|
|
|
|
for y in range(height):
|
|
if y & (1 << bit):
|
|
draw.line((0, y, width - 1, y), fill=color)
|
|
|
|
for y in (0, 31, 32, 63):
|
|
if y < height:
|
|
draw.line((0, y, width - 1, y), fill=(255, 255, 255))
|
|
return image
|