ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
make_fixture.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# SPDX-License-Identifier: MIT
3# Copyright (c) 2026 Brighton Sikarskie
4"""Build a tiny demo .cbz from a few generated page images.
5
6Draws three visually distinct portrait "comic" pages (a coloured gradient with a
7large page number and framing) and zips them into sample.cbz, so the viewer has a
8real archive to render for the rendering proof. Requires only Pillow.
9"""
10
11from __future__ import annotations
12
13import zipfile
14from pathlib import Path
15
16from PIL import Image, ImageDraw, ImageFont
17
18HERE = Path(__file__).resolve().parent
19PAGE_W, PAGE_H = 600, 900
20PAGES = [
21 ("page01.png", (240, 224, 200), (120, 60, 40), "1"),
22 ("page02.png", (200, 224, 240), (30, 60, 120), "2"),
23 ("page03.png", (210, 235, 210), (30, 110, 40), "3"),
24]
25
26# Candidate system faces, in preference order; falls back to Pillow's builtin.
27FONT_CANDIDATES = (
28 Path("/System/Library/Fonts/Supplemental/Arial Bold.ttf"),
29 Path("/System/Library/Fonts/Helvetica.ttc"),
30)
31
32# Vertical shading range applied down each page, in 8-bit levels.
33GRADIENT_DEPTH = 20
34
35
36def _font(size: int) -> ImageFont.ImageFont:
37 for path in FONT_CANDIDATES:
38 if path.exists():
39 return ImageFont.truetype(str(path), size)
40 return ImageFont.load_default()
41
42
43def make_page(bg: tuple[int, int, int], fg: tuple[int, int, int], label: str) -> Image.Image:
44 """Render one portrait demo page: gradient, border, banner and page number.
45
46 The output is intentionally busy. A flat page would render identically
47 whether the viewer decoded it correctly, scaled it wrongly, or filled the
48 frame with a solid error color; the gradient, the inset border and the
49 360pt numeral each make a different class of rendering bug visible by eye.
50
51 Not pixel-reproducible across machines: `_font` picks the first available
52 system face and falls back to Pillow's builtin, so glyph shapes differ by
53 host. Do not hash these pages -- they are a visual proof, not a golden.
54
55 Args:
56 bg: Background RGB triple. Each channel must be at least GRADIENT_DEPTH,
57 or the shading underflows past 0 and Pillow rejects the color.
58 fg: Foreground RGB triple for the border and all text.
59 label: Page-number text, drawn centred at 360pt. Practically one or two
60 characters; longer strings overflow the page.
61
62 Returns:
63 An RGB Pillow Image, PAGE_W by PAGE_H.
64 """
65 img = Image.new("RGB", (PAGE_W, PAGE_H), bg)
66 draw = ImageDraw.Draw(img)
67 for y in range(PAGE_H):
68 shade = int(GRADIENT_DEPTH * (y / PAGE_H))
69 draw.line(
70 [(0, y), (PAGE_W, y)],
71 fill=(bg[0] - shade, bg[1] - shade, bg[2] - shade),
72 )
73 draw.rectangle([20, 20, PAGE_W - 20, PAGE_H - 20], outline=fg, width=6)
74 draw.text((PAGE_W // 2, 80), "RA8 VIEWER", font=_font(48), fill=fg, anchor="mm")
75 draw.text((PAGE_W // 2, PAGE_H // 2), label, font=_font(360), fill=fg, anchor="mm")
76 draw.text(
77 (PAGE_W // 2, PAGE_H - 70),
78 "sample comic page",
79 font=_font(32),
80 fill=fg,
81 anchor="mm",
82 )
83 return img
84
85
86def main() -> None:
87 """Render every page in PAGES and zip them into sample.cbz beside this file.
88
89 Output lands next to the script rather than in the working directory, so the
90 fixture is regenerated in place wherever it is run from. An existing
91 sample.cbz is overwritten.
92
93 Each PNG is written to disk, added to the archive, then unlinked -- the
94 intermediates are a zipfile.write() requirement, not artifacts, and only
95 sample.cbz survives. A crash mid-loop leaves stray page*.png behind.
96
97 Page order in the archive is PAGES order, which is what the viewer reads as
98 reading order.
99 """
100 cbz = HERE / "sample.cbz"
101 with zipfile.ZipFile(cbz, "w", zipfile.ZIP_DEFLATED) as zf:
102 for name, bg, fg, label in PAGES:
103 png = HERE / name
104 make_page(bg, fg, label).save(png)
105 zf.write(png, name)
106 png.unlink()
107 print("wrote", cbz)
108
109
110if __name__ == "__main__":
111 main()
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298