ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
sh_comic.c
Go to the documentation of this file.
1
42#include <string.h>
43
44#include "comic.h"
45#include "ra8_gfx.h"
46#include "ra8_gfx_font.h"
47#include "reflow_image.h"
48#include "sh_app.h"
49#include "sh_comic_fixture.h"
50
52typedef enum : uint32_t {
54 k_shc_names_cap = 128U * 1024U,
55 k_shc_pagebuf_cap = 4U * 1024U * 1024U,
56 k_shc_arena_cap = 8U * 1024U * 1024U,
59 k_shc_probe_bg = 0x202028U,
61 k_shc_live_bg = 0x101010U,
62 k_shc_fnv_offset = 2166136261U,
63 k_shc_fnv_prime = 16777619U,
69
76typedef struct {
77 const uint8_t* d;
78 size_t n;
80
82[[gnu::section(".sdram_data"), gnu::aligned(8)]] static comic_t s_comic;
84[[gnu::section(".sdram_data"), gnu::aligned(8)]] static comic_page_t s_pages[k_shc_page_cap];
86[[gnu::section(".sdram_data"), gnu::aligned(8)]] static char s_names[k_shc_names_cap];
88[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_pagebuf[k_shc_pagebuf_cap];
90[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_arena[k_shc_arena_cap];
92[[gnu::section(".sdram_data"),
93 gnu::aligned(8)]] static uint16_t s_probe_rgb[(size_t)k_shc_probe_h * (size_t)k_shc_probe_w];
94
98static bool s_from_sd;
99
101// cppcheck-suppress constParameterCallback -- ctx is pinned by the comic_read_fn vtable signature (void* ctx forwarded verbatim into ra8_rar_open); const cannot cascade the shared read seam
102static size_t sh_comic_blob_read(void* ctx, uint64_t off, void* buf, size_t len)
103{
104 const sh_comic_blob_t* s = (const sh_comic_blob_t*)ctx;
105 if ((s == nullptr) || (buf == nullptr) || (off >= (uint64_t)s->n)) {
106 return 0U;
107 }
108 const uint64_t avail = (uint64_t)s->n - off;
109 const size_t k = (len > (size_t)avail) ? (size_t)avail : len;
110 (void)memcpy(buf, &s->d[off], k);
111 return k;
112}
113
115static uint32_t sh_comic_fnv(const void* p, size_t n)
116{
117 const uint8_t* b = (const uint8_t*)p;
118 uint32_t h = (uint32_t)k_shc_fnv_offset;
119 for (size_t i = 0U; i < n; ++i) {
120 h = (h ^ (uint32_t)b[i]) * (uint32_t)k_shc_fnv_prime;
121 }
122 return h;
123}
124
126static bool sh_comic_bind(comic_read_fn rd, void* ctx, uint64_t size)
127{
128 const ra8_err_t err = comic_open(&s_comic,
129 rd,
130 ctx,
131 size,
132 s_pages,
133 (uint32_t)k_shc_page_cap,
134 s_names,
135 (uint32_t)k_shc_names_cap);
136 if (err != k_ra8_ok) {
137 return false;
138 }
139 g_sh.comic_count = comic_page_count(&s_comic);
140 g_sh.comic_page = 0U;
141 return g_sh.comic_count > 0U;
142}
143
145static ra8_err_t sh_comic_blit_page(uint32_t page,
146 int32_t x,
147 int32_t y,
148 int32_t w,
149 int32_t h,
150 int32_t* out_w,
151 int32_t* out_h)
152{
153 if ((g_sh.comic_count == 0U) || (page >= g_sh.comic_count)) {
155 }
156 size_t got = 0U;
157 const ra8_err_t rerr = comic_page_read(&s_comic, page, s_pagebuf, sizeof s_pagebuf, &got);
158 if (rerr != k_ra8_ok) {
159 return rerr;
160 }
161 ra8_img_arena_t arena = {.base = s_arena, .cap = sizeof s_arena, .offset = 0U, .live = 0U};
162 int32_t dw = 0;
163 int32_t dh = 0;
164 const ra8_err_t derr = ra8_img_decode_blit(&arena, s_pagebuf, got, x, y, w, h, &dw, &dh);
165 if (out_w != nullptr) {
166 *out_w = dw;
167 }
168 if (out_h != nullptr) {
169 *out_h = dh;
170 }
171 return derr;
172}
173
174bool sh_comic_open(uint16_t idx)
175{
177 if (idx >= g_sh.book_count) {
178 return false;
179 }
180 const sh_entry_t* e = &g_sh.entry[idx];
181 if (!sh_fmt_is_comic(e->fmt)) {
182 return false;
183 }
184 if (e->from_sd) {
185 uint32_t len = 0U;
186 if (!sh_sd_book_open(e->sd_name, &len)) {
187 return false;
188 }
189 if (!sh_comic_bind(sh_sd_comic_read, nullptr, (uint64_t)len)) {
191 return false;
192 }
193 s_from_sd = true;
194 return true;
195 }
196 s_blob.d = e->blob;
197 s_blob.n = (size_t)e->blob_len;
198 if (!sh_comic_bind(sh_comic_blob_read, &s_blob, (uint64_t)e->blob_len)) {
199 return false;
200 }
201 s_from_sd = false;
202 return true;
203}
204
206{
207 (void)comic_close(&s_comic); /* idempotent after a failed / no open */
208 if (s_from_sd) {
210 s_from_sd = false;
211 }
212 g_sh.comic_count = 0U;
213 g_sh.comic_page = 0U;
214}
215
217static int32_t sh_comic_center_x(const char* s)
218{
219 return ((int32_t)k_sh_fb_w - ((int32_t)strlen(s) * (int32_t)k_sh_glyph_w)) / 2;
220}
221
223{
224 (void)ra8_gfx_clear((uint32_t)k_shc_live_bg);
225
226 char right[k_sh_linebuf];
227 size_t rp = 0U;
228 rp = sh_fmt_uint(right, rp, g_sh.comic_page + 1U);
229 right[rp++] = '/';
230 rp = sh_fmt_uint(right, rp, g_sh.comic_count);
231 right[rp++] = ' ';
232 right[rp++] = ' ';
233 const char* dir = g_sh.comic_rtl ? "RTL" : "LTR";
234 for (const char* s = dir; *s != '\0'; ++s) {
235 right[rp++] = *s;
236 }
237 right[rp] = '\0';
238 sh_titlebar("< Library", right);
239
240 const int32_t box_y = (int32_t)k_sh_bar_h;
241 const int32_t box_h = (int32_t)k_sh_fb_h - (int32_t)k_sh_bar_h;
242 const ra8_err_t err =
243 sh_comic_blit_page(g_sh.comic_page, 0, box_y, (int32_t)k_sh_fb_w, box_h, nullptr, nullptr);
244 if (err != k_ra8_ok) {
245 const char* msg = "Unable to render this page";
247 box_y + (box_h / 2),
248 msg,
250 (uint32_t)k_sh_col_sub,
251 (uint32_t)k_shc_live_bg);
252 }
253}
254
255int32_t sh_comic_edge_dir(int32_t x)
256{
257 const int32_t third = (int32_t)k_sh_fb_w / (int32_t)k_shc_thirds;
258 int32_t base = 0;
259 if (x < third) {
260 base = -1;
261 } else if (x >= ((int32_t)k_sh_fb_w - third)) {
262 base = 1;
263 }
264 /* Manga (RTL) mirrors the edges: the left edge advances, the right goes back. */
265 return g_sh.comic_rtl ? -base : base;
266}
267
268bool sh_comic_turn(int32_t dir)
269{
270 const uint32_t n = g_sh.comic_count;
271 if (n == 0U) {
272 return false;
273 }
274 if ((dir > 0) && ((g_sh.comic_page + 1U) < n)) {
275 g_sh.comic_page += 1U;
276 return true;
277 }
278 if ((dir < 0) && (g_sh.comic_page > 0U)) {
279 g_sh.comic_page -= 1U;
280 return true;
281 }
282 return false;
283}
284
285bool sh_comic_tap(int32_t x, bool header)
286{
287 if (header) {
288 /* The header's right half carries the "p/N LTR|RTL" indicator; tapping it
289 * flips the reading direction. The left half steps back to the shelf. */
290 if (x >= ((int32_t)k_sh_fb_w / 2)) {
292 } else {
293 g_sh.screen = k_sh_screen_shelf;
294 }
295 return true;
296 }
297 const int32_t d = sh_comic_edge_dir(x);
298 return (d != 0) && sh_comic_turn(d);
299}
300
302{
303 g_sh.comic_rtl = !g_sh.comic_rtl;
304}
305
307static sh_comic_probe_t sh_comic_probe_blob(const uint8_t* blob, uint32_t len)
308{
309 sh_comic_probe_t r = {};
310 s_blob.d = blob;
311 s_blob.n = (size_t)len;
312 s_from_sd = false;
313 if (!sh_comic_bind(sh_comic_blob_read, &s_blob, (uint64_t)len)) {
314 return r;
315 }
316 r.pages = g_sh.comic_count;
318 (uint16_t)k_shc_probe_w,
319 (uint16_t)k_shc_probe_h,
321 (void)ra8_gfx_clear((uint32_t)k_shc_probe_bg);
322 int32_t dw = 0;
323 int32_t dh = 0;
324 if (sh_comic_blit_page(0U, 0, 0, (int32_t)k_shc_probe_w, (int32_t)k_shc_probe_h, &dw, &dh) ==
325 k_ra8_ok) {
326 r.ok = true;
327 r.w = dw;
328 r.h = dh;
330 }
332 return r;
333}
334
336{
337 if ((cbz == nullptr) || (cbr == nullptr) || (rtl_ok == nullptr)) {
338 return;
339 }
342
343 const bool saved = g_sh.comic_rtl;
344 const int32_t right = (int32_t)k_sh_fb_w - 1;
345 g_sh.comic_rtl = false;
346 const int32_t ltr_right = sh_comic_edge_dir(right);
347 const int32_t ltr_left = sh_comic_edge_dir(0);
348 g_sh.comic_rtl = true;
349 const int32_t rtl_right = sh_comic_edge_dir(right);
350 const int32_t rtl_left = sh_comic_edge_dir(0);
351 g_sh.comic_rtl = saved;
352 *rtl_ok = (ltr_right == 1) && (ltr_left == -1) && (rtl_right == -1) && (rtl_left == 1);
353
354 /* Restore the live-framebuffer binding the probe rebound to the scratch. */
355 (void)
357}
358
360 size_t* pos,
361 const sh_comic_probe_t* cbz,
362 const sh_comic_probe_t* cbr,
363 bool rtl_ok)
364{
365 if ((b == nullptr) || (pos == nullptr) || (cbz == nullptr) || (cbr == nullptr)) {
366 return;
367 }
368 const sh_comic_probe_t* const set[] = {cbz, cbr};
369 const char* const tag[] = {" cbz=", " cbr="};
370 for (uint32_t i = 0U; i < (uint32_t)(sizeof(set) / sizeof(set[0])); ++i) {
371 size_t p = *pos;
372 for (const char* s = tag[i]; *s != '\0'; ++s) {
373 b[p++] = *s;
374 }
375 p = sh_fmt_uint(b, p, set[i]->pages);
376 b[p++] = ':';
377 p = sh_fmt_uint(b, p, (uint32_t)set[i]->w);
378 b[p++] = 'x';
379 p = sh_fmt_uint(b, p, (uint32_t)set[i]->h);
380 b[p++] = ':';
381 for (uint32_t d = 0U; d < (uint32_t)k_shc_hex_digits; ++d) {
382 const uint32_t nib =
383 (set[i]->crc >> (((uint32_t)k_shc_hex_digits - 1U - d) * (uint32_t)k_shc_nib_bits)) &
384 (uint32_t)k_shc_nib_mask;
385 b[p++] =
386 (char)((nib < k_sh_dec_base) ? ('0' + nib) : ('A' + (nib - (uint32_t)k_sh_dec_base)));
387 }
388 *pos = p;
389 }
390 const char* rtl = rtl_ok ? " rtl=OK" : " rtl=NO";
391 size_t p = *pos;
392 for (const char* s = rtl; *s != '\0'; ++s) {
393 b[p++] = *s;
394 }
395 *pos = p;
396}
static uint8_t s_arena[k_c6_cam_arena_bytes]
Definition c6_cam_app.c:43
Unified demand-paged reader for comic-book archives – CBZ (ZIP) and CBR (RAR).
ra8_err_t comic_page_read(comic_t *c, uint32_t page, uint8_t *buf, size_t cap, size_t *got)
Extract one page's encoded image bytes for the decoder.
Definition comic.c:472
size_t(* comic_read_fn)(void *ctx, uint64_t offset, void *buf, size_t len)
Seek+read backing over the comic-archive bytes.
Definition comic.h:95
ra8_err_t comic_close(comic_t *c)
Release an open comic's backend resources.
Definition comic.c:494
ra8_err_t comic_open(comic_t *c, comic_read_fn read, void *ctx, uint64_t size, comic_page_t *pages, uint32_t page_cap, char *names, uint32_t names_cap)
Open a comic archive (CBZ or CBR) and build its sorted page index.
Definition comic.c:390
static uint32_t comic_page_count(const comic_t *c)
Number of pages in an open comic.
Definition comic.h:263
static uint8_t s_blob[(size_t) k_demo_blob_cap]
Definition main.c:93
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
@ k_ra8_gfx_format_rgb565
16-bit RGB565, little-endian in memory.
Definition ra8_gfx.h:40
ra8_err_t ra8_gfx_clear(uint32_t color)
Fill the bound framebuffer's clip region with a single colour.
ra8_err_t ra8_gfx_text_out(int32_t x, int32_t y, const char *str, const ra8_gfx_font_t *font, uint32_t fg_color, uint32_t bg_color)
Render a NUL-terminated ASCII string.
ra8_err_t ra8_gfx_init(void *fb, uint16_t width, uint16_t height, ra8_gfx_format_t format)
Bind ra8_gfx to a caller-owned framebuffer.
Bitmap font descriptor + bundled font handles for ra8_gfx.
const ra8_gfx_font_t ra8_gfx_font_8x16
Bundled 8x16 IBM PC VGA bitmap font, ASCII 0x20..0x7E.
Zero-heap raster image decode + scale + blit for reflow (#106).
ra8_err_t ra8_img_decode_blit(ra8_img_arena_t *arena, const uint8_t *bytes, size_t len, int32_t dst_x, int32_t dst_y, int32_t box_w, int32_t box_h, int32_t *out_w, int32_t *out_h)
Decode bytes and blit it, scaled to fit, into the bound framebuffer.
Shared contract for the ereader_shelf multi-screen e-reader.
@ k_sh_screen_shelf
Book grid with cover thumbnails.
Definition sh_app.h:116
bool sh_sd_book_open(const char *name, uint32_t *out_len)
Open SD file name and hold it as the paged book backing.
Definition sh_sd.c:150
void sh_titlebar(const char *title, const char *right)
Fill the header bar and draw title left + optional right text.
Definition sh_util.c:76
size_t sh_sd_comic_read(void *ctx, uint64_t offset, void *buf, size_t len)
comic_read_fn over the held-open SD book file (seek + clamped read).
Definition sh_sd.c:203
@ k_sh_fb_h
Panel height in pixels.
Definition sh_app.h:47
@ k_sh_glyph_w
Bitmap font cell width.
Definition sh_app.h:48
@ k_sh_bar_h
Header / title-bar height.
Definition sh_app.h:51
@ k_sh_linebuf
Per-line draw buffer bytes.
Definition sh_app.h:59
@ k_sh_fb_w
Panel width in pixels.
Definition sh_app.h:46
@ k_sh_dec_base
Decimal formatting base.
Definition sh_app.h:71
size_t sh_fmt_uint(char *dst, size_t pos, uint32_t v)
Append unsigned v as decimal into dst at pos; returns new pos.
Definition sh_util.c:52
void sh_sd_book_close(void)
Close the held SD book file (idempotent).
Definition sh_sd.c:225
sh_state_t g_sh
The single whole-app state instance (defined in main.c).
Definition main.c:64
uint16_t * sh_fb_pixels(void)
The live RGB565 framebuffer ra8_gfx scans (defined in main.c).
Definition main.c:180
@ k_sh_col_sub
Secondary ink (author / hints).
Definition sh_app.h:103
static bool sh_fmt_is_comic(sh_book_fmt_t fmt)
True if fmt is a comic-archive container (CBZ, CBR, or CBT).
Definition sh_classify.h:59
bool sh_comic_tap(int32_t x, bool header)
Handle a comic-screen tap: header returns to the shelf, else page-turn.
Definition sh_comic.c:285
void sh_comic_render(void)
Render the current comic page full-screen (title bar + fitted image).
Definition sh_comic.c:222
static bool sh_comic_bind(comic_read_fn rd, void *ctx, uint64_t size)
Bind comic over rd + set g_sh.comic_count / page; ok on success.
Definition sh_comic.c:126
void sh_comic_selfcheck(sh_comic_probe_t *cbz, sh_comic_probe_t *cbr, bool *rtl_ok)
Headless boot self-check: decode the baked CBZ + CBR page 0, verify RTL.
Definition sh_comic.c:335
bool sh_comic_turn(int32_t dir)
Turn the comic page by dir in reading order, clamped to the ends.
Definition sh_comic.c:268
static comic_t s_comic
The open comic (large: page-index storage + backend scratch).
Definition sh_comic.c:82
void sh_comic_close(void)
Close any open comic and release its backing (idempotent).
Definition sh_comic.c:205
static size_t sh_comic_blob_read(void *ctx, uint64_t off, void *buf, size_t len)
comic_read_fn over a resident archive buffer (bounds-clamped).
Definition sh_comic.c:102
void sh_comic_toggle_rtl(void)
Flip the comic reading direction (LTR <-> RTL); page index unchanged.
Definition sh_comic.c:301
int32_t sh_comic_edge_dir(int32_t x)
Map a comic-screen edge tap to a reading-order page delta (RTL-aware).
Definition sh_comic.c:255
void sh_comic_append_banner(char *b, size_t *pos, const sh_comic_probe_t *cbz, const sh_comic_probe_t *cbr, bool rtl_ok)
Append " cbz=P:WxH:CRC cbr=P:WxH:CRC rtl=OK" to the boot banner buffer.
Definition sh_comic.c:359
static uint32_t sh_comic_fnv(const void *p, size_t n)
FNV-1a-32 over n bytes of p.
Definition sh_comic.c:115
static char s_names[k_shc_names_cap]
Caller-owned page-name arena.
Definition sh_comic.c:86
static comic_page_t s_pages[k_shc_page_cap]
Caller-owned sorted page index.
Definition sh_comic.c:84
static uint16_t s_probe_rgb[(size_t) k_shc_probe_h *(size_t) k_shc_probe_w]
Off-screen RGB565 scratch for the deterministic self-check decode.
Definition sh_comic.c:93
static ra8_err_t sh_comic_blit_page(uint32_t page, int32_t x, int32_t y, int32_t w, int32_t h, int32_t *out_w, int32_t *out_h)
Extract page page's encoded bytes and decode+blit into the box.
Definition sh_comic.c:145
static int32_t sh_comic_center_x(const char *s)
Centre-align s horizontally for the 8px bitmap font.
Definition sh_comic.c:217
static sh_comic_probe_t sh_comic_probe_blob(const uint8_t *blob, uint32_t len)
Open blob, decode its page 0 into the scratch, hash it; then close.
Definition sh_comic.c:307
static bool s_from_sd
true when the open comic's backing is a held SD file (close it too).
Definition sh_comic.c:98
static uint8_t s_pagebuf[k_shc_pagebuf_cap]
One page's extracted encoded image (the decoder input).
Definition sh_comic.c:88
sh_comic_const_t
Comic reader buffer + layout constants.
Definition sh_comic.c:52
@ k_shc_probe_bg
Self-check clear colour (matches.
Definition sh_comic.c:59
@ k_shc_nib_mask
Low-nibble mask.
Definition sh_comic.c:66
@ k_shc_thirds
Edge-tap split (left/centre/right).
Definition sh_comic.c:67
@ k_shc_hex_digits
Hex digits in a 32-bit CRC.
Definition sh_comic.c:64
@ k_shc_probe_h
Self-check scratch height, pixels.
Definition sh_comic.c:58
@ k_shc_nib_bits
Bits per hex nibble.
Definition sh_comic.c:65
@ k_shc_probe_w
Self-check scratch width, pixels.
Definition sh_comic.c:57
@ k_shc_fnv_prime
FNV-1a-32 prime.
Definition sh_comic.c:63
@ k_shc_live_bg
Live comic backdrop (near-black).
Definition sh_comic.c:61
@ k_shc_fnv_offset
FNV-1a-32 offset basis.
Definition sh_comic.c:62
@ k_shc_arena_cap
stb_image decode bump arena bytes.
Definition sh_comic.c:56
@ k_shc_page_cap
Max pages in the sorted index.
Definition sh_comic.c:53
@ k_shc_pagebuf_cap
One encoded page image, bytes.
Definition sh_comic.c:55
@ k_shc_names_cap
Page-name arena bytes.
Definition sh_comic.c:54
bool sh_comic_open(uint16_t idx)
Open shelf entry idx as a comic archive (CBZ / CBR) for page reading.
Definition sh_comic.c:174
Baked CBZ + CBR comic fixtures for the ereader_shelf comic self-check.
static const uint32_t k_comic_cbz_len
static const uint8_t k_comic_cbr[]
Baked 401-byte comic fixture.
static const uint8_t k_comic_cbz[]
Baked 576-byte comic fixture.
static const uint32_t k_comic_cbr_len
One entry in the resident, sorted page index.
Definition comic.h:140
One open comic archive: backing, kind, page index, and backend state.
Definition comic.h:179
Caller-owned bump arena backing a single image decode.
Resident-buffer backing for the comic reader's seek+read seam.
Definition sh_comic.c:76
size_t n
Archive length.
Definition sh_comic.c:78
const uint8_t * d
Archive bytes.
Definition sh_comic.c:77
One container's headless self-check result (page 0 decode digest).
Definition sh_app.h:686
uint32_t pages
Page count parsed from the archive.
Definition sh_app.h:688
bool ok
true if page 0 opened + decoded successfully.
Definition sh_app.h:687
int32_t w
Blitted (fitted) page width in pixels.
Definition sh_app.h:689
int32_t h
Blitted (fitted) page height in pixels.
Definition sh_app.h:690
uint32_t crc
FNV-1a-32 over the decoded scratch framebuffer.
Definition sh_app.h:691
One shelf book, sourced from MRAM (baked) or the SD card, in either the .rabook or ....
Definition sh_app.h:141
bool from_sd
true: read sd_name from SD; false: use blob.
Definition sh_app.h:142
const uint8_t * blob
Baked RBKC bytes (MRAM), or NULL when SD.
Definition sh_app.h:144
uint32_t blob_len
Baked length, or SD file size in bytes.
Definition sh_app.h:145
char sd_name[k_sh_name_cap]
SD file name, e.g.
Definition sh_app.h:149
sh_book_fmt_t fmt
Container format (rabook / epub).
Definition sh_app.h:143