ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_v2.cpp
Go to the documentation of this file.
1
20
21#include <cstddef>
22#include <cstdint>
23#include <cstdio>
24#include <cstring>
25#include <memory>
26#include <string>
27
28extern "C" {
29#include "ra8_attributes.h"
30#include "ra8_err.h"
31#include "reflow.h"
32}
33
34#include "litehtml.h"
35
36namespace {
37// NOLINTBEGIN(readability-static-definition-in-anonymous-namespace) -- the
38// RA8_INTERNAL contract requires literal static linkage so the annotations
39// gate can verify it; the anonymous namespace alone does not satisfy it.
40
41typedef enum : uint8_t {
42 k_v2_glyph_w_px = 8U,
43 k_v2_image_default_w_px = 64U,
44 k_v2_image_default_h_px = 64U,
45 k_v2_pt_to_px_num = 4U,
46 k_v2_pt_to_px_den = 3U,
47 k_v2_ascent_num = 4U,
48 k_v2_ascent_den = 5U,
49 k_v2_descent_den = 5U,
50 k_v2_xheight_den = 2U,
51 k_v2_media_resolution_dpi = 96U,
52 k_v2_html_wrap_reserve = 64U,
53} v2_metrics_t;
54
55class v2_container : public litehtml::document_container {
56public:
57 v2_container(int viewport_w, int viewport_h, int font_px)
58 : viewport_w_(viewport_w), viewport_h_(viewport_h), font_px_(font_px)
59 {}
60
61 litehtml::uint_ptr create_font(const litehtml::font_description& /*descr*/,
62 const litehtml::document* /*doc*/,
63 litehtml::font_metrics* fm) override
64 {
65 if (fm != nullptr) {
66 fm->font_size = static_cast<litehtml::pixel_t>(font_px_);
67 fm->height = static_cast<litehtml::pixel_t>(font_px_);
68 fm->ascent = (static_cast<litehtml::pixel_t>(font_px_) *
69 static_cast<litehtml::pixel_t>(k_v2_ascent_num)) /
70 static_cast<litehtml::pixel_t>(k_v2_ascent_den);
71 fm->descent =
72 static_cast<litehtml::pixel_t>(font_px_) / static_cast<litehtml::pixel_t>(k_v2_descent_den);
73 fm->x_height =
74 static_cast<litehtml::pixel_t>(font_px_) / static_cast<litehtml::pixel_t>(k_v2_xheight_den);
75 fm->ch_width = static_cast<int>(k_v2_glyph_w_px);
76 fm->draw_spaces = true;
77 }
78 return 1U;
79 }
80 void delete_font(litehtml::uint_ptr /*hFont*/) override {}
81 litehtml::pixel_t text_width(const char* t, litehtml::uint_ptr /*hFont*/) override
82 {
83 return t ? static_cast<litehtml::pixel_t>(std::strlen(t)) *
84 static_cast<litehtml::pixel_t>(k_v2_glyph_w_px)
85 : 0;
86 }
87 void draw_text(litehtml::uint_ptr /*hdc*/,
88 const char* /*text*/,
89 litehtml::uint_ptr /*hFont*/,
90 litehtml::web_color /*color*/,
91 const litehtml::position& /*pos*/) override
92 {}
93 litehtml::pixel_t pt_to_px(float pt) const override
94 {
95 return static_cast<litehtml::pixel_t>((pt * static_cast<float>(k_v2_pt_to_px_num)) /
96 static_cast<float>(k_v2_pt_to_px_den));
97 }
98 litehtml::pixel_t get_default_font_size() const override
99 {
100 return static_cast<litehtml::pixel_t>(font_px_);
101 }
102 const char* get_default_font_name() const override
103 {
104 return "sans-serif";
105 }
106 void draw_list_marker(litehtml::uint_ptr /*hdc*/,
107 const litehtml::list_marker& /*marker*/) override
108 {}
109 void load_image(const char* /*src*/, const char* /*baseurl*/, bool /*redraw_on_ready*/) override
110 {}
111 void get_image_size(const char* /*src*/, const char* /*baseurl*/, litehtml::size& sz) override
112 {
113 sz.width = static_cast<int>(k_v2_image_default_w_px);
114 sz.height = static_cast<int>(k_v2_image_default_h_px);
115 }
116 void draw_image(litehtml::uint_ptr /*hdc*/,
117 const litehtml::background_layer& /*layer*/,
118 const std::string& /*url*/,
119 const std::string& /*base_url*/) override
120 {}
121 void draw_solid_fill(litehtml::uint_ptr /*hdc*/,
122 const litehtml::background_layer& /*layer*/,
123 const litehtml::web_color& /*color*/) override
124 {}
125 void
126 draw_linear_gradient(litehtml::uint_ptr /*hdc*/,
127 const litehtml::background_layer& /*layer*/,
128 const litehtml::background_layer::linear_gradient& /*gradient*/) override
129 {}
130 void
131 draw_radial_gradient(litehtml::uint_ptr /*hdc*/,
132 const litehtml::background_layer& /*layer*/,
133 const litehtml::background_layer::radial_gradient& /*gradient*/) override
134 {}
135 void draw_conic_gradient(litehtml::uint_ptr /*hdc*/,
136 const litehtml::background_layer& /*layer*/,
137 const litehtml::background_layer::conic_gradient& /*gradient*/) override
138 {}
139 void draw_borders(litehtml::uint_ptr /*hdc*/,
140 const litehtml::borders& /*borders*/,
141 const litehtml::position& /*draw_pos*/,
142 bool /*root*/) override
143 {}
144 void set_caption(const char* /*caption*/) override {}
145 void set_base_url(const char* /*base_url*/) override {}
146 void link(const std::shared_ptr<litehtml::document>& /*doc*/,
147 const litehtml::element::ptr& /*el*/) override
148 {}
149 void on_anchor_click(const char* /*url*/, const litehtml::element::ptr& /*el*/) override {}
150 void on_mouse_event(const litehtml::element::ptr& /*el*/,
151 litehtml::mouse_event /*event*/) override
152 {}
153 void set_cursor(const char* /*cursor*/) override {}
154 void transform_text(litehtml::string& /*text*/, litehtml::text_transform /*tt*/) override {}
155 void import_css(litehtml::string& t,
156 const litehtml::string& /*url*/,
157 litehtml::string& /*baseurl*/) override
158 {
159 t.clear();
160 }
161 void set_clip(const litehtml::position& /*pos*/,
162 const litehtml::border_radiuses& /*bdr_radius*/) override
163 {}
164 void del_clip() override {}
165 void get_viewport(litehtml::position& v) const override
166 {
167 v.x = 0;
168 v.y = 0;
169 v.width = static_cast<litehtml::pixel_t>(viewport_w_);
170 v.height = static_cast<litehtml::pixel_t>(viewport_h_);
171 }
172 litehtml::element::ptr create_element(const char* /*tag_name*/,
173 const litehtml::string_map& /*attributes*/,
174 const std::shared_ptr<litehtml::document>& /*doc*/) override
175 {
176 return nullptr;
177 }
178 void get_media_features(litehtml::media_features& m) const override
179 {
180 m = litehtml::media_features{};
181 m.type = litehtml::media_type_screen;
182 m.width = static_cast<litehtml::pixel_t>(viewport_w_);
183 m.height = static_cast<litehtml::pixel_t>(viewport_h_);
184 m.device_width = static_cast<litehtml::pixel_t>(viewport_w_);
185 m.device_height = static_cast<litehtml::pixel_t>(viewport_h_);
186 m.color = 8;
187 m.resolution = static_cast<int>(k_v2_media_resolution_dpi);
188 }
189 void get_language(litehtml::string& l, litehtml::string& c) const override
190 {
191 l = "en";
192 c = "";
193 }
194
195private:
196 int viewport_w_;
197 int viewport_h_;
198 int font_px_;
199};
200
201struct v2_state {
202 /* Field order matters for destruction: the document holds a raw
203 * pointer to the container (passed via container.get() into
204 * createFromString), so the document must be destroyed first.
205 * In C++ struct destruction runs in reverse declaration order,
206 * so list the container last. */
207 litehtml::document::ptr document;
208 std::unique_ptr<v2_container> container;
209};
210
226RA8_INTERNAL static inline void internal_v2_state_clear(v2_state& s)
227{
228 s.document.reset();
229 s.container.reset();
230}
231
232typedef enum : uint8_t {
233 k_v2_max_engines = 4U
234} v2_cache_caps_t;
235
236struct v2_cache_slot {
237 const reflow_t* engine = nullptr;
238 v2_state state;
239};
240
241v2_cache_slot s_v2_cache[k_v2_max_engines];
242
258RA8_INTERNAL static v2_state* internal_v2_state_for(const reflow_t* engine, bool create)
259{
260 for (uint8_t i = 0; i < (uint8_t)k_v2_max_engines; ++i) {
261 if (s_v2_cache[i].engine == engine) {
262 return &s_v2_cache[i].state;
263 }
264 }
265 if (!create) {
266 return nullptr;
267 }
268 for (uint8_t i = 0; i < (uint8_t)k_v2_max_engines; ++i) {
269 if (s_v2_cache[i].engine == nullptr) {
270 s_v2_cache[i].engine = engine;
271 internal_v2_state_clear(s_v2_cache[i].state);
272 return &s_v2_cache[i].state;
273 }
274 }
275 return nullptr;
276}
277
289RA8_INTERNAL static void internal_v2_state_drop(const reflow_t* engine)
290{
291 for (uint8_t i = 0; i < (uint8_t)k_v2_max_engines; ++i) {
292 if (s_v2_cache[i].engine == engine) {
293 internal_v2_state_clear(s_v2_cache[i].state);
294 s_v2_cache[i].engine = nullptr;
295 return;
296 }
297 }
298}
299
314RA8_INTERNAL static ra8_err_t internal_check_engine(const reflow_t* engine)
315{
316 if (engine == nullptr) {
317 return k_ra8_err_null_ptr;
318 }
319 if (engine->in_use != 1U) {
321 }
322 return k_ra8_ok;
323}
324
342RA8_INTERNAL static ra8_err_t internal_v2_build_document(v2_state* state, const reflow_t* engine)
343{
344 state->document.reset();
345 state->container.reset();
346
347 state->container = std::make_unique<v2_container>(static_cast<int>(engine->viewport_w),
348 static_cast<int>(engine->viewport_h),
349 static_cast<int>(engine->font_px));
350
351 const std::string body(reinterpret_cast<const char*>(engine->xhtml_buf), engine->xhtml_len);
352 std::string html;
353 html.reserve(body.size() + k_v2_html_wrap_reserve);
354 html.append("<!DOCTYPE html><html><head></head><body>");
355 html.append(body);
356 html.append("</body></html>");
357
358 const litehtml::estring src(html);
359 state->document = litehtml::document::createFromString(src, state->container.get());
360 return state->document ? k_ra8_ok : k_ra8_err_validation_failed;
361}
362
376RA8_INTERNAL static void internal_v2_paginate(reflow_t* engine, int content_height)
377{
378 const int vh = static_cast<int>(engine->viewport_h);
379 int pages = (content_height + vh - 1) / vh;
380 if (pages < 1) {
381 pages = 1;
382 }
383 if (pages > static_cast<int>(k_reflow_max_pages)) {
384 pages = static_cast<int>(k_reflow_max_pages);
385 }
386 engine->page_count = static_cast<uint32_t>(pages);
387 for (int i = 0; i < pages; ++i) {
388 engine->pages[i].glyph_first = 0U;
389 engine->pages[i].glyph_count = 0U;
390 }
391}
392
408RA8_INTERNAL static ra8_err_t internal_v2_run_layout(reflow_t* engine, uint32_t* out_total_pages)
409{
410 v2_state* state = internal_v2_state_for(engine, true);
411 if (state == nullptr) {
412 return k_ra8_err_no_mem;
413 }
414 const ra8_err_t err = internal_v2_build_document(state, engine);
415 if (err != k_ra8_ok) {
416 return err;
417 }
418
419 (void)state->document->render(static_cast<litehtml::pixel_t>(engine->viewport_w));
420 int content_height = static_cast<int>(state->document->height());
421 if (content_height < 0) {
422 content_height = 0;
423 }
424
425 internal_v2_paginate(engine, content_height);
426 if (out_total_pages != nullptr) {
427 *out_total_pages = engine->page_count;
428 }
429 return k_ra8_ok;
430}
431
432// NOLINTEND(readability-static-definition-in-anonymous-namespace)
433} // namespace
434
435extern "C" {
436
437[[nodiscard]] ra8_err_t reflow_init(uint16_t viewport_w,
438 uint16_t viewport_h,
439 const uint8_t* font_data,
440 size_t font_len,
441 uint16_t font_px,
442 uint32_t body_color,
443 uint32_t link_color,
444 reflow_t* out_engine)
445{
446 if ((font_data == nullptr) || (out_engine == nullptr)) {
447 return k_ra8_err_null_ptr;
448 }
449 if ((viewport_w == 0U) || (viewport_h == 0U) || (font_px < (uint16_t)k_reflow_min_font_px) ||
450 (font_px > (uint16_t)k_reflow_max_font_px)) {
451 (void)std::memset(out_engine, 0, sizeof(*out_engine));
453 }
454 if (font_len < 16U) {
455 (void)std::memset(out_engine, 0, sizeof(*out_engine));
457 }
458 (void)std::memset(out_engine, 0, sizeof(*out_engine));
459 out_engine->viewport_w = viewport_w;
460 out_engine->viewport_h = viewport_h;
461 out_engine->font_px = font_px;
462 out_engine->body_color = body_color;
463 out_engine->link_color = link_color;
464 out_engine->font_data = font_data;
465 out_engine->font_len = font_len;
466 out_engine->in_use = 1U;
467 return k_ra8_ok;
468}
469
470[[nodiscard]] ra8_err_t reflow_close(reflow_t* engine)
471{
472 if (engine == nullptr) {
473 return k_ra8_err_null_ptr;
474 }
475 if (engine->in_use != 1U) {
477 }
478 internal_v2_state_drop(engine);
479 engine->in_use = 0U;
480 engine->page_count = 0U;
481 return k_ra8_ok;
482}
483
485 const uint8_t* xhtml_buf,
486 size_t xhtml_len,
487 uint32_t* out_total_pages)
488{
489 const ra8_err_t err = internal_check_engine(engine);
490 if (err != k_ra8_ok) {
491 return err;
492 }
493 if ((xhtml_buf == nullptr) || (out_total_pages == nullptr)) {
494 return k_ra8_err_null_ptr;
495 }
496 if (xhtml_len == 0U) {
498 }
499 engine->xhtml_buf = xhtml_buf;
500 engine->xhtml_len = xhtml_len;
501 return internal_v2_run_layout(engine, out_total_pages);
502}
503
504[[nodiscard]] ra8_err_t
505reflow_render_page(const reflow_t* engine, uint32_t page_idx, void* framebuffer)
506{
507 (void)framebuffer;
508 const ra8_err_t err = internal_check_engine(engine);
509 if (err != k_ra8_ok) {
510 return err;
511 }
512 if (page_idx >= engine->page_count) {
514 }
515 return k_ra8_ok;
516}
517
518[[nodiscard]] ra8_err_t reflow_get_page_count(const reflow_t* engine, uint32_t* out_count)
519{
520 const ra8_err_t err = internal_check_engine(engine);
521 if (err != k_ra8_ok) {
522 return err;
523 }
524 if (out_count == nullptr) {
525 return k_ra8_err_null_ptr;
526 }
527 *out_count = engine->page_count;
528 return k_ra8_ok;
529}
530
531[[nodiscard]] ra8_err_t reflow_set_font_size(reflow_t* engine, uint16_t new_font_px)
532{
533 const ra8_err_t err = internal_check_engine(engine);
534 if (err != k_ra8_ok) {
535 return err;
536 }
537 if ((new_font_px < (uint16_t)k_reflow_min_font_px) ||
538 (new_font_px > (uint16_t)k_reflow_max_font_px)) {
540 }
541 if (engine->xhtml_buf == nullptr) {
543 }
544 engine->font_px = new_font_px;
545 uint32_t total = 0U;
546 return internal_v2_run_layout(engine, &total);
547}
548
549[[nodiscard]] ra8_err_t
550reflow_parse_xhtml(reflow_t* engine, const uint8_t* xhtml_buf, size_t xhtml_len)
551{
552 if (engine == nullptr) {
553 return k_ra8_err_null_ptr;
554 }
555 engine->xhtml_buf = xhtml_buf;
556 engine->xhtml_len = xhtml_len;
557 return k_ra8_ok;
558}
559
561{
562 if (engine == nullptr) {
563 return k_ra8_err_null_ptr;
564 }
565 uint32_t total = 0U;
566 return internal_v2_run_layout(engine, &total);
567}
568
569} // extern "C"
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
HTML / CSS reflow + paginate engine for the ra8d2 ereader.
@ k_reflow_max_pages
Max paginated pages.
@ k_reflow_min_font_px
Smallest accepted font size.
@ k_reflow_max_font_px
Largest accepted font size.
ra8_err_t reflow_render_page(const reflow_t *engine, uint32_t page_idx, void *framebuffer)
Render one page into the active ra8_gfx framebuffer.
ra8_err_t reflow_run_layout(reflow_t *engine)
Run the line-break + page-break pass over engine->tokens[].
ra8_err_t reflow_layout_chapter(reflow_t *engine, const uint8_t *xhtml_buf, size_t xhtml_len, uint32_t *out_total_pages)
Parse + lay out one chapter of XHTML.
ra8_err_t reflow_parse_xhtml(reflow_t *engine, const uint8_t *xhtml_buf, size_t xhtml_len)
Parse the XHTML buffer into the engine's token stream.
ra8_err_t reflow_set_font_size(reflow_t *engine, uint16_t new_font_px)
Change the body font size and re-flow the cached chapter.
ra8_err_t reflow_init(uint16_t viewport_w, uint16_t viewport_h, const uint8_t *font_data, size_t font_len, uint16_t font_px, uint32_t body_color, uint32_t link_color, reflow_t *out_engine)
Initialise a reflow engine for a given viewport / font.
ra8_err_t reflow_get_page_count(const reflow_t *engine, uint32_t *out_count)
Report the laid-out page count.
ra8_err_t reflow_close(reflow_t *engine)
Release a previously initialized engine.
uint32_t glyph_count
Number of glyphs in this page.
uint32_t glyph_first
Index of first glyph in this page.
Reflow / pagination engine state.
uint32_t page_count
Pages used.
const uint8_t * font_data
TTF blob; outlives the engine.
uint32_t link_color
Anchor text colour (0xRRGGBB).
reflow_page_t pages[k_reflow_max_pages]
Page index ranges.
uint8_t in_use
1 = initialized, 0 = closed.
size_t font_len
Length of font_data, bytes.
uint16_t font_px
Body font size in pixels.
uint16_t viewport_w
Viewport width, pixels.
const uint8_t * xhtml_buf
Last layout_chapter input.
uint16_t viewport_h
Viewport height, pixels.
size_t xhtml_len
Length of xhtml_buf.
uint32_t body_color
Body text colour (0xRRGGBB).