win/lib/shared/media.py

2.4 KB · 83 lines · python

1#!/usr/bin/env python3
2"""ASCII image fallback for brt -image."""
3
4import sys
5
6
7ASCII_CHARS = "@%#*+=-:. "
8
9
10def load_ppm(path: str) -> tuple[int, int, bytes]:
11 with open(path, "rb") as f:
12 magic = f.readline().strip()
13 if magic not in (b"P6", b"P5"):
14 raise ValueError(f"Unsupported PPM magic: {magic!r}")
15
16 def _read_line() -> bytes:
17 line = f.readline()
18 while line.startswith(b"#"):
19 line = f.readline()
20 return line
21
22 dims = _read_line().decode().strip().split()
23 width, height = int(dims[0]), int(dims[1])
24 maxval = int(_read_line().decode().strip())
25 if maxval > 255:
26 raise ValueError("Only 8-bit PPM is supported")
27
28 if magic == b"P6":
29 data = f.read(width * height * 3)
30 else:
31 gray = f.read(width * height)
32 data = bytes(ch for px in gray for ch in (px, px, px))
33 return width, height, data
34
35
36def render_ascii(path: str, width: int, height: int = 0) -> None:
37 img_w, img_h, rgb = load_ppm(path)
38 if width <= 0:
39 width = min(100, img_w)
40
41 aspect = img_h / img_w
42 if height <= 0:
43 height = max(1, int(width * aspect * 0.55))
44 out = []
45
46 for y in range(height):
47 row = []
48 for x in range(width):
49 src_x = min(img_w - 1, int(x * img_w / width))
50 src_y = min(img_h - 1, int(y * img_h / height))
51 idx = (src_y * img_w + src_x) * 3
52 r, g, b = rgb[idx], rgb[idx + 1], rgb[idx + 2]
53 lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
54 char_idx = int(lum / 255 * (len(ASCII_CHARS) - 1))
55 row.append(ASCII_CHARS[char_idx])
56 out.append("".join(row))
57 print("\n".join(out))
58
59
60def main() -> int:
61 if len(sys.argv) < 2:
62 print("usage: media.py ascii <ppm-file> <width> [height]", file=sys.stderr)
63 return 1
64
65 mode = sys.argv[1]
66 if mode != "ascii":
67 print("usage: media.py ascii <ppm-file> <width> [height]", file=sys.stderr)
68 return 1
69
70 if len(sys.argv) < 4:
71 print("usage: media.py ascii <ppm-file> <width> [height]", file=sys.stderr)
72 return 1
73
74 path = sys.argv[2]
75 width = int(sys.argv[3])
76 height = int(sys.argv[4]) if len(sys.argv) > 4 else 0
77 render_ascii(path, width, height)
78 return 0
79
80
81if __name__ == "__main__":
82 raise SystemExit(main())
83