ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
rabook_gray8_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"""Generate the ereader_rabook full-resolution gray8 image fixture (#476).
5
6The ereader_rabook HIL/ra8_emulator gate proves the compiled-book path. This
7generator bakes a second, tiny `.rabook` blob whose one raster image is stored
8at ``PIXFMT_GRAY8`` -- the full-resolution, continuous-tone representation the
9compiler retains for zoomable content (#476), never the panel-quantised 4bpp
10form. The blob is emitted INFLATED (``BlobBuilder.serialize`` output, no RBKC
11container) so the gate can ``book_validate`` and walk it with no
12decompressor, exactly like the sibling ``rabook_fixture.h``.
13
14The image is a synthetic diagonal grayscale ramp: its pixel values sweep the
15full 0-255 range, so many land strictly between two 16-level gray4 steps. A
16gray4 store would round them to the grid; keeping them verbatim is the proof
17that the compiled blob retains continuous tone at full source resolution. The
18image itself is grayscale ("L" mode) so the host decode is deterministic across
19Pillow versions (no colour->luma fold), which keeps the baked bytes -- and the
20ra8_emulator framebuffer golden derived from them -- reproducible.
21
22Regenerate with ``just tools::rabook_fixture`` after any format change,
23then re-pin the ereader_rabook ``img`` framebuffer golden (its hil.conf
24``HIL_EXPECT``) in the same change.
25
26@copyright Copyright (c) 2026 Brighton Sikarskie
27SPDX-License-Identifier: MIT
28"""
29
30from __future__ import annotations
31
32import io
33import sys
34from pathlib import Path
35
36from PIL import Image
37
38_REPO_ROOT = Path(__file__).resolve().parents[2]
39_EPUB_COMPILE = _REPO_ROOT / "tools" / "epub_compile" / "src"
40sys.path.insert(0, str(_EPUB_COMPILE))
41
42from rabook_blob import BlobBuilder # noqa: E402 -- imagepack path added above
43from rabook_format import PIXFMT_GRAY8 # noqa: E402 -- imagepack path added above
44
45# Image geometry: small enough to render 1:1 inside the gate's 128x160 RGB565
46# framebuffer, large enough that the diagonal ramp visits many off-grid tones.
47_IMG_W = 96
48_IMG_H = 48
49_GRAY_MAX = 255 # Peak 8-bit gray value.
50_HALF = 2 # Averaging divisor for the two-axis ramp.
51_ARRAY_LINE_LIMIT = 96 # Soft wrap width for the emitted C byte rows.
52
53_OUT = (
54 _REPO_ROOT
55 / "examples"
56 / "ek_ra8d2"
57 / "hw_validated"
58 / "hil"
59 / "ereader_rabook"
60 / "inc"
61 / "rabook_gray8_fixture.h"
62)
63
64
65def _ramp_png() -> bytes:
66 """Encode a WxH grayscale diagonal ramp as PNG bytes.
67
68 Returns:
69 PNG-encoded bytes of an "L" (8-bit gray) image whose pixel (x, y) is the
70 average of a horizontal and a vertical 0-255 ramp, so tone varies
71 smoothly on both axes and covers values off the 16-level gray4 grid.
72 """
73 im = Image.new("L", (_IMG_W, _IMG_H))
74 px = [
75 ((x * _GRAY_MAX) // (_IMG_W - 1) + (y * _GRAY_MAX) // (_IMG_H - 1)) // _HALF
76 for y in range(_IMG_H)
77 for x in range(_IMG_W)
78 ]
79 im.putdata(px)
80 buf = io.BytesIO()
81 im.save(buf, "PNG")
82 return buf.getvalue()
83
84
85def _emit_array(name: str, data: bytes) -> str:
86 """Render bytes as a 4-byte-aligned ``static const uint8_t`` C array.
87
88 ``alignas(4)`` matters: the firmware casts into the blob to read 32-bit
89 header fields, and an unaligned load faults on the target. Deterministic for
90 identical input, so the generated header commits and diffs cleanly.
91
92 Args:
93 name: C identifier for the array.
94 data: Bytes to emit.
95
96 Returns:
97 The declaration as a newline-joined string (no trailing newline).
98 """
99 out = [f"alignas(4) static const uint8_t {name}[{len(data)}U] = {{"]
100 line = " "
101 for b in data:
102 line += f"0x{b:02X}U,"
103 if len(line) >= _ARRAY_LINE_LIMIT:
104 out.append(line)
105 line = " "
106 if line.strip():
107 out.append(line)
108 out.append("};")
109 return "\n".join(out)
110
111
112def _build_blob() -> bytes:
113 """Compile the one-image gray8 book to an inflated flat `.rabook` blob."""
114 bb = BlobBuilder()
115 idx = bb.add_raster_image("ramp.png", _ramp_png(), pixel_format=PIXFMT_GRAY8)
116 bb.cover_index = idx
117 meta = {
118 "title": "gray8 ramp",
119 "author": "",
120 "language": "en",
121 "identifier": "urn:ra8:rabook-gray8-fixture",
122 }
123 return bb.serialize(meta)
124
125
126def main() -> int:
127 """Emit the gray8 fixture header; return a process exit code."""
128 blob = _build_blob()
129 header = "\n".join(
130 [
131 "/**",
132 " * @file rabook_gray8_fixture.h",
133 " * @brief Baked, INFLATED .rabook flat blob carrying one full-resolution",
134 " * gray8 image (#476).",
135 " * @generated by scripts/gen/rabook_gray8_fixture.py -- do not edit by hand.",
136 " *",
137 " * @details",
138 " * The compiled-book path retains zoomable rasters at full source resolution",
139 " * in continuous-tone gray8 (1 byte/pixel), never the panel-quantised 4bpp",
140 " * form. This one-image book is the ra8_emulator proof: ereader_rabook validates",
141 " * it, confirms the image is 8bpp and holds more than the 16 distinct tones a",
142 " * 4bpp store could reproduce, then blits it 1:1 at full resolution. The image",
143 " * is a diagonal grayscale ramp whose tones sweep the full 0-255 range, so its",
144 " * survival proves no quantisation occurred. Do not edit by hand; see the",
145 " * generator.",
146 " *",
147 " * @copyright Copyright (c) 2026 Brighton Sikarskie",
148 " * SPDX-License-Identifier: MIT",
149 " * @since Version 0.1.0",
150 " */",
151 "#pragma once",
152 "",
153 "#include <stdint.h>",
154 "",
155 "/**",
156 " * @enum rabook_gray8_fixture_size_t",
157 " * @brief Byte length of @ref k_rabook_gray8_fixture.",
158 " * @since Version 0.1.0",
159 " */",
160 "typedef enum : uint32_t {",
161 f" k_rabook_gray8_fixture_len = {len(blob)}U, /**< Inflated flat-blob length. */",
162 "} rabook_gray8_fixture_size_t;",
163 "",
164 "/** @brief Baked, inflated gray8-image .rabook blob (validate + walk). */",
165 "// NOLINTBEGIN(readability-magic-numbers) -- generated byte-table fixture.",
166 ]
167 )
168 body = _emit_array("k_rabook_gray8_fixture", blob)
169 text = header + "\n" + body + "\n// NOLINTEND(readability-magic-numbers)\n"
170 _OUT.write_text(text, encoding="ascii")
171 sys.stdout.write(f"rabook_gray8_fixture: wrote {_OUT} ({len(blob)} blob bytes)\n")
172 return 0
173
174
175if __name__ == "__main__":
176 raise SystemExit(main())
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298