ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
sh_reader.c
Go to the documentation of this file.
1
19#include <string.h>
20
21#include "ra8_gfx.h"
22#include "ra8_gfx_font.h"
23#include "sh_app.h"
24
29typedef struct {
30 uint32_t cp;
31 const char* rep;
32} sh_fold_t;
33
35static const sh_fold_t k_sh_fold[] = {
36 {0x2014U, "--"}, {0x2013U, "-"}, {0x2012U, "-"}, {0x2010U, "-"}, {0x2011U, "-"},
37 {0x2018U, "'"}, {0x2019U, "'"}, {0x201AU, "'"}, {0x2032U, "'"}, {0x201CU, "\""},
38 {0x201DU, "\""}, {0x201EU, "\""}, {0x2033U, "\""}, {0x2026U, "..."}, {0x00A0U, " "},
39 {0x2007U, " "}, {0x2009U, " "}, {0x202FU, " "}, {0x200AU, " "}, {0x2002U, " "},
40 {0x2003U, " "},
41};
42
44typedef enum : uint32_t {
45 k_sh_u8_ascii = 0x80U,
46 k_sh_u8_lead2 = 0xC0U,
47 k_sh_u8_lead3 = 0xE0U,
48 k_sh_u8_lead4 = 0xF0U,
49 k_sh_u8_mask2 = 0xE0U,
50 k_sh_u8_mask3 = 0xF0U,
51 k_sh_u8_mask4 = 0xF8U,
52 k_sh_u8_data2 = 0x1FU,
53 k_sh_u8_data3 = 0x0FU,
54 k_sh_u8_cont = 0x3FU,
55 k_sh_u8_repl = 0xFFFDU,
56} sh_u8_t;
57
59typedef enum : uint8_t {
63
65static size_t sh_utf8_decode(const uint8_t* s, size_t n, uint32_t* cp)
66{
67 const uint8_t b = s[0];
68 if (b < (uint8_t)k_sh_u8_ascii) {
69 *cp = b;
70 return 1U;
71 }
72 if ((((uint32_t)b & k_sh_u8_mask2) == k_sh_u8_lead2) && (n >= 2U)) {
73 *cp = (((uint32_t)b & k_sh_u8_data2) << k_sh_u8_sh6) | ((uint32_t)s[1] & k_sh_u8_cont);
74 return 2U;
75 }
76 if ((((uint32_t)b & k_sh_u8_mask3) == k_sh_u8_lead3) && (n >= 3U)) {
77 *cp = (((uint32_t)b & k_sh_u8_data3) << k_sh_u8_sh12) |
78 (((uint32_t)s[1] & k_sh_u8_cont) << k_sh_u8_sh6) | ((uint32_t)s[2] & k_sh_u8_cont);
79 return 3U;
80 }
81 *cp = k_sh_u8_repl;
82 return ((((uint32_t)b & k_sh_u8_mask4) == k_sh_u8_lead4) && (n >= 4U)) ? 4U : 1U;
83}
84
86static const char* sh_fold_lookup(uint32_t cp)
87{
88 for (size_t i = 0U; i < (sizeof(k_sh_fold) / sizeof(k_sh_fold[0])); ++i) {
89 if (k_sh_fold[i].cp == cp) {
90 return k_sh_fold[i].rep;
91 }
92 }
93 return "";
94}
95
97static void sh_ascii_fold(void)
98{
99 size_t r = 0U;
100 size_t w = 0U;
101 while (r < g_sh.text_len) {
102 uint32_t cp = 0U;
103 const size_t adv = sh_utf8_decode((const uint8_t*)&g_sh.text[r], g_sh.text_len - r, &cp);
104 r += adv;
105 if (cp < k_sh_u8_ascii) {
106 g_sh.text[w++] = (char)cp;
107 continue;
108 }
109 for (const char* p = sh_fold_lookup(cp); *p != '\0'; ++p) {
110 g_sh.text[w++] = *p; /* every rep is no longer than its UTF-8 source: w <= r */
111 }
112 }
113 g_sh.text_len = w;
114}
115
117static size_t sh_skip_word(size_t i)
118{
119 while ((i < g_sh.text_len) && (g_sh.text[i] != ' ') && (g_sh.text[i] != '\n')) {
120 ++i;
121 }
122 return i;
123}
124
126static size_t sh_wrap_line(size_t i, int32_t chars, size_t* ls, size_t* le)
127{
128 while ((i < g_sh.text_len) && (g_sh.text[i] == ' ')) {
129 ++i;
130 }
131 *ls = i;
132 *le = i;
133 int32_t col = 0;
134 while (i < g_sh.text_len) {
135 if (g_sh.text[i] == '\n') {
136 ++i;
137 break;
138 }
139 const size_t ws = i;
140 const size_t we = sh_skip_word(i);
141 const int32_t need = (col > 0 ? col + 1 : 0) + (int32_t)(we - ws);
142 if ((col > 0) && (need > chars)) {
143 i = ws;
144 break;
145 }
146 i = ((we < g_sh.text_len) && (g_sh.text[we] == ' ')) ? (we + 1U) : we;
147 *le = we;
148 col = need;
149 }
150 return i;
151}
152
154static void sh_reader_wrap(int32_t chars)
155{
156 g_sh.line_count = 0U;
157 size_t i = 0U;
158 while ((i < g_sh.text_len) && (g_sh.line_count < (uint32_t)k_sh_max_lines)) {
159 size_t ls = 0U;
160 size_t le = 0U;
161 i = sh_wrap_line(i, chars, &ls, &le);
162 if (ls >= g_sh.text_len) {
163 break;
164 }
165 g_sh.lines[g_sh.line_count].off = (uint32_t)ls;
166 g_sh.lines[g_sh.line_count].len = (uint16_t)(le - ls);
167 g_sh.line_count++;
168 }
169}
170
172static uint32_t sh_lines_per_page(void)
173{
174 return (uint32_t)(((int32_t)k_sh_fb_h - (int32_t)k_sh_bar_h) / (int32_t)k_sh_line_h);
175}
176
177void sh_reader_load_chapter(uint32_t chapter)
178{
179 g_sh.chapter = chapter;
180 g_sh.page = 0U;
181 g_sh.text_len = 0U;
182 size_t tl = 0U;
183 if (sh_book_chapter_text(chapter, g_sh.text, sizeof g_sh.text, &tl) == k_ra8_ok) {
184 g_sh.text_len = tl;
185 }
187 sh_reader_wrap(sh_cells((int32_t)k_sh_fb_w - (2 * (int32_t)k_sh_pad)));
188 const uint32_t lpp = sh_lines_per_page();
189 uint32_t pages = (lpp == 0U) ? 1U : ((g_sh.line_count + lpp - 1U) / lpp);
190 g_sh.chap_pages = (pages == 0U) ? 1U : pages;
191}
192
194typedef enum : uint32_t {
197
199{
200 for (uint32_t c = 0U; c < g_sh.chapter_count; ++c) {
201 size_t tl = 0U;
202 if ((sh_book_chapter_text(c, g_sh.text, sizeof g_sh.text, &tl) == k_ra8_ok) &&
203 (tl > (size_t)k_sh_content_min)) {
204 return c;
205 }
206 }
207 return 0U;
208}
209
211{
212 (void)ra8_gfx_clear((uint32_t)k_sh_col_card);
213 char right[k_sh_linebuf];
214 size_t rp = 0U;
215 right[rp++] = 'C';
216 right[rp++] = 'h';
217 right[rp++] = ' ';
218 rp = sh_fmt_uint(right, rp, g_sh.chapter + 1U);
219 right[rp++] = '/';
220 rp = sh_fmt_uint(right, rp, g_sh.chapter_count);
221 right[rp++] = ' ';
222 right[rp++] = ' ';
223 rp = sh_fmt_uint(right, rp, g_sh.page + 1U);
224 right[rp++] = '/';
225 rp = sh_fmt_uint(right, rp, g_sh.chap_pages);
226 right[rp] = '\0';
227 sh_titlebar("< Contents", right);
228
229 const uint32_t lpp = sh_lines_per_page();
230 const uint32_t first = g_sh.page * lpp;
231 char line[k_sh_linebuf];
232 for (uint32_t r = 0U; r < lpp; ++r) {
233 const uint32_t li = first + r;
234 if (li >= g_sh.line_count) {
235 break;
236 }
237 uint16_t len = g_sh.lines[li].len;
238 if (len >= (uint16_t)sizeof line) {
239 len = (uint16_t)sizeof line - 1U;
240 }
241 memcpy(line, &g_sh.text[g_sh.lines[li].off], len);
242 line[len] = '\0';
243 const int32_t y =
244 (int32_t)k_sh_bar_h + (int32_t)k_sh_card_pad + ((int32_t)r * (int32_t)k_sh_line_h);
245 (void)ra8_gfx_text_out((int32_t)k_sh_pad,
246 y,
247 line,
249 (uint32_t)k_sh_col_ink,
250 (uint32_t)k_sh_col_card);
251 }
252}
253
254bool sh_reader_turn(int32_t dir)
255{
256 if (dir > 0) {
257 if ((g_sh.page + 1U) < g_sh.chap_pages) {
258 g_sh.page++;
259 return true;
260 }
261 if ((g_sh.chapter + 1U) < g_sh.chapter_count) {
262 sh_reader_load_chapter(g_sh.chapter + 1U);
263 return true;
264 }
265 return false;
266 }
267 if (dir < 0) {
268 if (g_sh.page > 0U) {
269 g_sh.page--;
270 return true;
271 }
272 if (g_sh.chapter > 0U) {
273 sh_reader_load_chapter(g_sh.chapter - 1U);
274 g_sh.page = (g_sh.chap_pages > 0U) ? (g_sh.chap_pages - 1U) : 0U;
275 return true;
276 }
277 return false;
278 }
279 return false;
280}
281
283{
284 if (g_sh.open_fmt != k_sh_fmt_rabook) {
285 return; /* EPUB reads through a different backend -- no ra8_vmem cache to warm. */
286 }
287 if (g_sh.book_src.vm == nullptr) {
288 return; /* resident/unbound source -- nothing to page in. */
289 }
290 const uint32_t ch = g_sh.chapter;
291 if ((ch + 1U) < g_sh.chapter_count) {
292 (void)book_src_prefetch_chapter(&g_sh.book_src, ch + 1U); /* warm N+1 */
293 }
294 if (ch > 0U) {
295 (void)book_src_prefetch_chapter(&g_sh.book_src, ch - 1U); /* warm N-1 */
296 }
297}
ra8_err_t book_src_prefetch_chapter(const book_src_t *src, uint32_t chapter_idx)
Warm the cache frame holding a chapter's first content bytes, without pinning it (single-threaded rea...
Definition book_paged.c:721
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
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...
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.
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.
Shared contract for the ereader_shelf multi-screen e-reader.
int32_t sh_cells(int32_t pixels)
Cells of the 8px bitmap font that fit in pixels.
Definition sh_util.c:28
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
ra8_err_t sh_book_chapter_text(uint32_t chapter, char *out, size_t cap, size_t *out_len)
Extract one chapter's plain text from the open book (either backend).
Definition sh_book.c:480
@ k_sh_line_h
Reader text line height.
Definition sh_app.h:50
@ k_sh_fb_h
Panel height in pixels.
Definition sh_app.h:47
@ k_sh_pad
Outer margin / content inset.
Definition sh_app.h:52
@ 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_max_lines
Max wrapped reader lines per chapter.
Definition sh_app.h:61
@ k_sh_fb_w
Panel width in pixels.
Definition sh_app.h:46
@ k_sh_card_pad
Inner card inset.
Definition sh_app.h:54
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
sh_state_t g_sh
The single whole-app state instance (defined in main.c).
Definition main.c:64
@ k_sh_col_ink
Body ink.
Definition sh_app.h:102
@ k_sh_col_card
Card / page fill (paper).
Definition sh_app.h:100
@ k_sh_fmt_rabook
book RBKC container (demand-paged chunks).
Definition sh_classify.h:38
static size_t sh_skip_word(size_t i)
Skip the next word in the text at i; returns the index past it.
Definition sh_reader.c:117
bool sh_reader_turn(int32_t dir)
Turn the reader page by dir (-1/+1) across chapters; returns changed.
Definition sh_reader.c:254
uint32_t sh_reader_first_content(void)
First chapter with substantial prose (skips title/imprint front matter).
Definition sh_reader.c:198
void sh_reader_load_chapter(uint32_t chapter)
Load + wrap a chapter into the reader working set.
Definition sh_reader.c:177
sh_u8_t
UTF-8 lead/continuation masks.
Definition sh_reader.c:44
@ k_sh_u8_data3
3-byte payload bits.
Definition sh_reader.c:53
@ k_sh_u8_lead2
2-byte lead prefix.
Definition sh_reader.c:46
@ k_sh_u8_mask3
3-byte lead mask.
Definition sh_reader.c:50
@ k_sh_u8_ascii
Below this: bare ASCII.
Definition sh_reader.c:45
@ k_sh_u8_lead4
4-byte lead prefix.
Definition sh_reader.c:48
@ k_sh_u8_lead3
3-byte lead prefix.
Definition sh_reader.c:47
@ k_sh_u8_repl
U+FFFD replacement.
Definition sh_reader.c:55
@ k_sh_u8_data2
2-byte payload bits.
Definition sh_reader.c:52
@ k_sh_u8_mask4
4-byte lead mask.
Definition sh_reader.c:51
@ k_sh_u8_mask2
2-byte lead mask.
Definition sh_reader.c:49
@ k_sh_u8_cont
Continuation payload bits.
Definition sh_reader.c:54
static void sh_ascii_fold(void)
Fold sh_state_t::text in place from UTF-8 to ASCII.
Definition sh_reader.c:97
static uint32_t sh_lines_per_page(void)
Reader lines that fit on one page.
Definition sh_reader.c:172
static void sh_reader_wrap(int32_t chars)
Greedy word-wrap sh_state_t::text into sh_state_t::lines.
Definition sh_reader.c:154
void sh_reader_render(void)
Render the current reader page.
Definition sh_reader.c:210
void sh_reader_prefetch_adjacent(void)
Read-ahead warm the adjacent chapters into the page cache (#207).
Definition sh_reader.c:282
static const char * sh_fold_lookup(uint32_t cp)
ASCII replacement for code point cp; "" to drop it.
Definition sh_reader.c:86
static const sh_fold_t k_sh_fold[]
Punctuation the ASCII bitmap font cannot draw, folded to ASCII.
Definition sh_reader.c:35
static size_t sh_wrap_line(size_t i, int32_t chars, size_t *ls, size_t *le)
Wrap one line from i; store [*ls,*le); return the next index.
Definition sh_reader.c:126
sh_content_const_t
Front-matter skip threshold.
Definition sh_reader.c:194
@ k_sh_content_min
Min chars to treat a chapter as readable prose.
Definition sh_reader.c:195
sh_u8_shift_t
Continuation-byte shifts.
Definition sh_reader.c:59
@ k_sh_u8_sh12
Two continuation bytes.
Definition sh_reader.c:61
@ k_sh_u8_sh6
One continuation byte.
Definition sh_reader.c:60
static size_t sh_utf8_decode(const uint8_t *s, size_t n, uint32_t *cp)
Decode one UTF-8 sequence at s; sets cp, returns bytes consumed.
Definition sh_reader.c:65
One typographic-Unicode -> ASCII substitution.
Definition sh_reader.c:29
const char * rep
ASCII replacement ("" drops the char).
Definition sh_reader.c:31
uint32_t cp
Source Unicode code point.
Definition sh_reader.c:30