4"""Generate the baked oversized single-page CBZ fixture for ereader_comic (#344).
6Emits a pure 7-bit-ASCII C header (comic_large_fixture.h) holding one CBZ with a
7single deliberately-large grayscale PNG page. Decoded at full resolution the page
8far exceeds the app's whole-decode arena, so the reader cannot open it that way;
9it exercises the tile path (comic_tiles -> JOF atlas -> ra8_tile_cache)
10instead. The art is flat-shaded horizontal bands plus a frame and a left gutter,
11so it DEFLATEs to a few hundred bytes and the baked array stays tiny on disk
12while the decoded image is multiple megabytes.
14The output is deterministic (fixed art, fixed zlib level), so the tile digest the
15app prints is identical on host, ra8_emulator, and silicon.
18 python3 scripts/gen/gen_comic_large_fixture.py \
19 examples/ek_ra8d2/hw_pending/ereader_comic/inc/comic_large_fixture.h
22from __future__
import annotations
27from pathlib
import Path
37 Path(__file__).resolve().parents[2]
43 /
"comic_large_fixture.h"
45ZIP_LOCAL_MAGIC = 0x04034B50
46ZIP_CENTRAL_MAGIC = 0x02014B50
47ZIP_EOCD_MAGIC = 0x06054B50
50DEFLATE_RAW_WBITS = -15
62def gray(x: int, y: int) -> int:
63 """Deterministic, DEFLATE-friendly grayscale sample for one pixel."""
64 if x < BORDER
or x >= PAGE_W - BORDER
or y < BORDER
or y >= PAGE_H - BORDER:
68 band = (y * BAND_COUNT) // PAGE_H
69 return min(255, BAND_BASE + BAND_STEP * band)
73 """Render the whole page into a flat grayscale buffer."""
74 out = bytearray(PAGE_W * PAGE_H)
75 for y
in range(PAGE_H):
77 for x
in range(PAGE_W):
78 out[row + x] = gray(x, y)
82def png_chunk(tag: bytes, data: bytes) -> bytes:
83 """Wrap a payload in one PNG chunk: length, type, data, CRC-32."""
84 out = struct.pack(
">I", len(data)) + tag + data
85 crc = zlib.crc32(tag + data) & 0xFFFFFFFF
86 return out + struct.pack(
">I", crc)
89def encode_png(g: bytes | bytearray) -> bytes:
90 """8-bit grayscale PNG (color type 0), one filter-0 byte per scanline."""
91 sig = b
"\x89PNG\r\n\x1a\n"
92 ihdr = struct.pack(
">IIBBBBB", PAGE_W, PAGE_H, PNG_BIT_DEPTH_8, PNG_COLOR_GRAY, 0, 0, 0)
94 for y
in range(PAGE_H):
96 raw += g[y * PAGE_W : (y + 1) * PAGE_W]
97 idat = zlib.compress(bytes(raw), ZLIB_BEST)
98 return sig + png_chunk(b
"IHDR", ihdr) + png_chunk(b
"IDAT", idat) + png_chunk(b
"IEND", b
"")
101def build_cbz(png: bytes) -> bytes:
102 """Minimal deterministic single-member ZIP (DEFLATE) that miniz reads."""
103 name = b
"01_large.png"
104 comp = zlib.compressobj(ZLIB_BEST, zlib.DEFLATED, DEFLATE_RAW_WBITS)
105 body = comp.compress(png) + comp.flush()
106 crc = zlib.crc32(png) & 0xFFFFFFFF
127 "<IHHHHHHIIIHHHHHII",
159 return local + central + eocd
162def emit_header(path: str | Path, cbz: bytes) ->
None:
163 """Write the generated pure-ASCII C header holding the CBZ byte array."""
166 " * @file comic_large_fixture.h\n"
167 " * @brief Baked oversized single-page CBZ for the ereader_comic tile self-check\n"
168 " * (#344; generated by scripts/gen/gen_comic_large_fixture.py -- do not edit).\n"
170 f
" * @details A miniz-decodable ZIP of one {PAGE_W}x{PAGE_H} grayscale PNG page whose\n"
171 " * decoded size far exceeds the reader's whole-decode arena, so it can only\n"
172 " * be opened through the JOF tile path (comic_tiles). Flat-shaded art\n"
173 " * keeps the baked array tiny while the decoded image is multiple MiB.\n"
175 " * @copyright Copyright (c) 2026 Brighton Sikarskie\n"
176 " * SPDX-License-Identifier: MIT\n"
179 "#include <stdint.h>\n\n"
180 f
"/** @brief Baked {len(cbz)}-byte single-page oversized CBZ. */\n"
181 "static const uint8_t k_comic_large_cbz[] = {\n"
184 for start
in range(0, len(cbz), BYTES_PER_ROW):
185 chunk = cbz[start : start + BYTES_PER_ROW]
186 rows.append(
" " +
" ".join(f
"0x{byte:02X}," for byte
in chunk))
192 "/** @brief Length of ::k_comic_large_cbz in bytes. */\n"
193 "static const uint32_t k_comic_large_cbz_len = (uint32_t)sizeof(k_comic_large_cbz);\n\n"
195 " * @brief Decoded geometry of the baked oversized page.\n"
196 " * @details These are properties of the baked bytes above, not tunables: the\n"
197 " * decoder must reproduce exactly this size or the fixture is wrong.\n"
199 "typedef enum : uint32_t {\n"
200 f
" k_comic_large_w = {PAGE_W}U, /**< Decoded width in pixels. */\n"
201 f
" k_comic_large_h = {PAGE_H}U, /**< Decoded height in pixels. */\n"
202 "} comic_large_geometry_t;\n"
204 Path(path).write_text(header +
"\n".join(rows) + footer, encoding=
"ascii")
208 """Render the oversized page, pack it into a CBZ, write the C header."""
209 out = Path(sys.argv[1])
if len(sys.argv) > 1
else DEFAULT_OUTPUT
211 emit_header(out, cbz)
212 sys.stderr.write(f
"wrote {out} ({len(cbz)} bytes CBZ, {PAGE_W}x{PAGE_H})\n")
215if __name__ ==
"__main__":
static bool render_page(uint8_t *fb, const char *text, uint32_t len)
Render the collected page text into the SDRAM framebuffer via ra8_gfx.
void main(void)
The application entry point Reset_Handler hands control to.
#define min(x, y)
Untyped minimum shim used by the SOUP's buffer clamping.