from __future__ import annotations from PIL import Image, ImageDraw, ImageFont from .text_renderer import load_font WIFI_INDICATOR_BRIGHTNESS = 50 WIFI_SCROLL_PIXELS_PER_SECOND = 16.0 WIFI_SCROLL_FPS = 20.0 WIFI_SCROLL_GAP_PIXELS = 16 WIFI_TEXT_SIZE = 10 def _draw_binary_text( image: Image.Image, position: tuple[int, int], message: str, *, font: ImageFont.ImageFont, ) -> None: probe = ImageDraw.Draw(image) bbox = probe.textbbox((0, 0), message, font=font) width = max(1, bbox[2] - bbox[0]) height = max(1, bbox[3] - bbox[1]) mask = Image.new("L", (width, height), 0) ImageDraw.Draw(mask).text((-bbox[0], -bbox[1]), message, font=font, fill=255) # Promote every covered font sample to a complete LED pixel. At this size, # retaining grayscale antialiasing makes thin strokes disappear at 50%. mask = mask.point(lambda value: 255 if value >= 64 else 0) image.paste((255, 255, 255), (position[0] + bbox[0], position[1] + bbox[1]), mask) def text_width(message: str) -> int: font = load_font("default", WIFI_TEXT_SIZE, message) draw = ImageDraw.Draw(Image.new("RGB", (1, 1))) box = draw.textbbox((0, 0), message, font=font) return max(1, box[2] - box[0]) def _draw_disconnected_icon(draw: ImageDraw.ImageDraw) -> None: # A compact WiFi fan with a red slash remains recognizable on 64x64. white = (255, 255, 255) red = (255, 48, 48) draw.arc((19, 4, 45, 28), 205, 335, fill=white, width=2) draw.arc((23, 10, 41, 28), 205, 335, fill=white, width=2) draw.arc((28, 17, 36, 27), 205, 335, fill=white, width=2) draw.rectangle((31, 27, 33, 29), fill=white) draw.line((19, 4, 45, 30), fill=red, width=3) def render_wifi_indicator( message: str, *, offset: float, connected: bool, size: tuple[int, int] = (64, 64), ) -> Image.Image: image = Image.new("RGB", size, (0, 0, 0)) draw = ImageDraw.Draw(image) font = load_font("default", WIFI_TEXT_SIZE, message) if not connected: _draw_disconnected_icon(draw) x = size[0] - int(offset) y = 26 if connected else 42 _draw_binary_text(image, (x, y), message, font=font) return image