ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_svg.c
Go to the documentation of this file.
1
21
22#include "reflow_svg.h"
23
24#include <string.h>
25
26#include "ra8_attributes.h"
27#include "reflow_svg_internal.h"
28
29/* ===========================================================================
30 * Pure string / number helpers
31 * ===========================================================================
32 */
33
58bool priv_ra8_svgp_ws(char c)
59{
60 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') || (c == '\f');
61}
62
88char priv_ra8_svgp_lc(char c)
89{
90 return (char)(((c >= 'A') && (c <= 'Z')) ? (c + ('a' - 'A')) : c);
91}
92
123bool priv_ra8_svgp_starts_ci(const uint8_t* s, size_t len, size_t at, const char* lit)
124{
125 const size_t n = strlen(lit);
126 if ((at + n) > len) {
127 return false;
128 }
129 for (size_t k = 0U; k < n; ++k) {
130 if (priv_ra8_svgp_lc((char)s[at + k]) != priv_ra8_svgp_lc(lit[k])) {
131 return false;
132 }
133 }
134 return true;
135}
136
166size_t priv_ra8_svgp_find_ci(const uint8_t* s, size_t len, size_t from, const char* lit)
167{
168 const size_t n = strlen(lit);
169 if (n == 0U) {
170 return len;
171 }
172 size_t i = from;
173 /* Bounded: i advances by 1 each step, capped by len - n + 1. */
174 while ((i + n) <= len) {
175 if (priv_ra8_svgp_starts_ci(s, len, i, lit)) {
176 return i;
177 }
178 ++i;
179 }
180 return len;
181}
182
211static uint8_t internal_hex(char c)
212{
213 if ((c >= '0') && (c <= '9')) {
214 const uint32_t code = (uint32_t)(uint8_t)c;
215 return (uint8_t)(code - (uint32_t)(uint8_t)'0');
216 }
217 const char l = priv_ra8_svgp_lc(c);
218 if ((l >= 'a') && (l <= 'f')) {
219 const uint32_t code = (uint32_t)(uint8_t)l;
220 return (uint8_t)((code - (uint32_t)(uint8_t)'a') + (uint32_t)k_svg_hex_a10);
221 }
222 return (uint8_t)k_svg_hex_base;
223}
224
255int32_t priv_ra8_svgp_num(const uint8_t* s, size_t len, size_t* i)
256{
257 while ((*i < len) && (priv_ra8_svgp_ws((char)s[*i]) || (s[*i] == (uint8_t)','))) {
258 ++(*i);
259 }
260 int32_t sign = 1;
261 if ((*i < len) && ((s[*i] == (uint8_t)'-') || (s[*i] == (uint8_t)'+'))) {
262 sign = (s[*i] == (uint8_t)'-') ? INT32_C(-1) : INT32_C(1);
263 ++(*i);
264 }
265 int32_t v = 0;
266 while ((*i < len) && (s[*i] >= (uint8_t)'0') && (s[*i] <= (uint8_t)'9')) {
267 v = (v * (int32_t)k_svg_dec) + ((int32_t)s[*i] - (int32_t)'0');
268 ++(*i);
269 }
270 /* Skip a fractional part (truncated). */
271 if ((*i < len) && (s[*i] == (uint8_t)'.')) {
272 ++(*i);
273 while ((*i < len) && (s[*i] >= (uint8_t)'0') && (s[*i] <= (uint8_t)'9')) {
274 ++(*i);
275 }
276 }
277 return v * sign;
278}
279
280/* ===========================================================================
281 * Attribute + colour parsing
282 * ===========================================================================
283 */
284
316static bool internal_attr_at(const uint8_t* s, size_t len, size_t at, const char* name)
317{
318 const size_t n = strlen(name);
319 if (!priv_ra8_svgp_starts_ci(s, len, at, name)) {
320 return false;
321 }
322 if ((at > 0U) && !priv_ra8_svgp_ws((char)s[at - 1U])) {
323 return false; /* must be preceded by whitespace */
324 }
325 const size_t after = at + n;
326 if (after >= len) {
327 return false;
328 }
329 return (s[after] == (uint8_t)'=') || priv_ra8_svgp_ws((char)s[after]);
330}
331
364bool priv_ra8_svgp_attr(const uint8_t* s, size_t len, const char* name, size_t* voff, size_t* vlen)
365{
366 /* Bounded: i advances by 1 each step, capped by len. */
367 for (size_t i = 0U; i < len; ++i) {
368 if (!internal_attr_at(s, len, i, name)) {
369 continue;
370 }
371 size_t j = i + strlen(name);
372 while ((j < len) && (s[j] != (uint8_t)'=')) {
373 ++j;
374 }
375 ++j; /* past '=' */
376 while ((j < len) && priv_ra8_svgp_ws((char)s[j])) {
377 ++j;
378 }
379 if ((j >= len) || ((s[j] != (uint8_t)'"') && (s[j] != (uint8_t)'\''))) {
380 return false;
381 }
382 const char q = (char)s[j];
383 ++j;
384 const size_t start = j;
385 while ((j < len) && (s[j] != (uint8_t)q)) {
386 ++j;
387 }
388 *voff = start;
389 *vlen = j - start;
390 return true;
391 }
392 return false;
393}
394
424int32_t priv_ra8_svgp_attr_num(const uint8_t* s, size_t len, const char* name, int32_t def)
425{
426 size_t off = 0U;
427 size_t vl = 0U;
428 if (!priv_ra8_svgp_attr(s, len, name, &off, &vl)) {
429 return def;
430 }
431 size_t k = 0U;
432 return priv_ra8_svgp_num(&s[off], vl, &k);
433}
434
464static bool internal_ci_eq(const uint8_t* s, size_t len, const char* lit)
465{
466 size_t k = 0U;
467 for (; (k < len) && (lit[k] != '\0'); ++k) {
468 if (priv_ra8_svgp_lc((char)s[k]) != priv_ra8_svgp_lc(lit[k])) {
469 return false;
470 }
471 }
472 return (k == len) && (lit[k] == '\0');
473}
474
505static uint32_t internal_hex_color(const uint8_t* s, size_t len)
506{
507 uint32_t rgb = 0U;
508 if (len == (size_t)k_svg_hex6) {
509 for (size_t i = 0U; i < len; ++i) {
510 const uint8_t v = internal_hex((char)s[i]);
511 if (v >= (uint8_t)k_svg_hex_base) {
512 return (uint32_t)k_svg_no_paint;
513 }
514 rgb = (rgb << (uint32_t)k_svg_hex_nib) | (uint32_t)v;
515 }
516 return rgb;
517 }
518 if (len == (size_t)k_svg_hex3) {
519 for (size_t i = 0U; i < len; ++i) {
520 const uint8_t v = internal_hex((char)s[i]);
521 if (v >= (uint8_t)k_svg_hex_base) {
522 return (uint32_t)k_svg_no_paint;
523 }
524 rgb =
525 (rgb << (uint32_t)k_svg_hex_chan) | ((uint32_t)v << (uint32_t)k_svg_hex_nib) | (uint32_t)v;
526 }
527 return rgb;
528 }
529 return (uint32_t)k_svg_no_paint;
530}
531
561static uint32_t internal_named_color(const uint8_t* s, size_t len)
562{
563 static const struct {
564 const char* name;
565 uint32_t rgb;
566 } k_names[] = {
567 {"black", 0x000000U},
568 {"white", 0xFFFFFFU},
569 {"red", 0xFF0000U},
570 {"green", 0x008000U},
571 {"blue", 0x0000FFU},
572 {"gray", 0x808080U},
573 {"grey", 0x808080U},
574 {"silver", 0xC0C0C0U},
575 {"maroon", 0x800000U},
576 {"navy", 0x000080U},
577 {"yellow", 0xFFFF00U},
578 {"orange", 0xFFA500U},
579 };
580 for (size_t k = 0U; k < (sizeof(k_names) / sizeof(k_names[0])); ++k) {
581 if (internal_ci_eq(s, len, k_names[k].name)) {
582 return k_names[k].rgb;
583 }
584 }
585 return (uint32_t)k_svg_no_paint;
586}
587
614uint32_t priv_ra8_svgp_paint(const uint8_t* s, size_t len)
615{
616 if (len == 0U) {
617 return (uint32_t)k_svg_no_paint;
618 }
619 if (s[0] == (uint8_t)'#') {
620 return internal_hex_color(&s[1], len - 1U);
621 }
622 return internal_named_color(s, len);
623}
624
654uint32_t priv_ra8_svgp_attr_paint(const uint8_t* s, size_t len, const char* name, uint32_t def)
655{
656 size_t off = 0U;
657 size_t vl = 0U;
658 if (!priv_ra8_svgp_attr(s, len, name, &off, &vl)) {
659 return def;
660 }
661 return priv_ra8_svgp_paint(&s[off], vl);
662}
663
665int32_t priv_match_grad(const svg_grads_t* grads, const uint8_t* val, size_t vlen)
666{
667 if ((grads == nullptr) || !priv_ra8_svgp_starts_ci(val, vlen, 0U, "url(#")) {
668 return -1;
669 }
670 const size_t idoff = strlen("url(#");
671 size_t idend = idoff;
672 while ((idend < vlen) && (val[idend] != (uint8_t)')')) {
673 ++idend;
674 }
675 const size_t idlen = idend - idoff;
676 /* Bounded: <= grads->n (<= k_svg_grad_max) gradients. */
677 for (int32_t i = 0; i < grads->n; ++i) {
678 const char* gid = grads->g[i].id;
679 bool equal = strlen(gid) == idlen;
680 for (size_t k = 0U; (k < idlen) && equal; ++k) {
681 equal = (uint8_t)gid[k] == val[idoff + k];
682 }
683 if (equal) {
684 return i;
685 }
686 }
687 return -1;
688}
689
721uint32_t priv_ra8_svgp_resolve_fill(const uint8_t* s,
722 size_t len,
723 const svg_xform_t* t,
724 uint32_t def,
725 int32_t* gi)
726{
727 *gi = -1;
728 size_t off = 0U;
729 size_t vl = 0U;
730 if (!priv_ra8_svgp_attr(s, len, "fill", &off, &vl)) {
731 return def;
732 }
733 if (priv_ra8_svgp_starts_ci(&s[off], vl, 0U, "url(#")) {
734 *gi = priv_match_grad(t->grads, &s[off], vl);
735 return (uint32_t)k_svg_no_paint;
736 }
737 return priv_ra8_svgp_paint(&s[off], vl);
738}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
size_t strlen(const char *s)
Calculate string length.
static bool internal_attr_at(const uint8_t *s, size_t len, size_t at, const char *name)
Test whether an attribute keyword begins at s[at] on a word boundary.
Definition reflow_svg.c:316
uint32_t priv_ra8_svgp_paint(const uint8_t *s, size_t len)
Parse an SVG paint value ('rgb'/'rrggbb'/name/'none') to 0x00RRGGBB.
Definition reflow_svg.c:614
int32_t priv_ra8_svgp_num(const uint8_t *s, size_t len, size_t *i)
Parse the integer part of one SVG number at s[*i], skipping leading separators (whitespace / commas);...
Definition reflow_svg.c:255
static uint8_t internal_hex(char c)
Convert a single hexadecimal digit character to its numeric value.
Definition reflow_svg.c:211
bool priv_ra8_svgp_ws(char c)
Test whether a character is XML whitespace.
Definition reflow_svg.c:58
bool priv_ra8_svgp_attr(const uint8_t *s, size_t len, const char *name, size_t *voff, size_t *vlen)
Find attribute name in tag span s[0..len); return its value slice.
Definition reflow_svg.c:364
uint32_t priv_ra8_svgp_resolve_fill(const uint8_t *s, size_t len, const svg_xform_t *t, uint32_t def, int32_t *gi)
Resolve a shape's 'fill': a solid colour, or a gradient index via gi.
Definition reflow_svg.c:721
uint32_t priv_ra8_svgp_attr_paint(const uint8_t *s, size_t len, const char *name, uint32_t def)
Read an SVG attribute as a paint colour, returning a default when absent.
Definition reflow_svg.c:654
size_t priv_ra8_svgp_find_ci(const uint8_t *s, size_t len, size_t from, const char *lit)
Find the first case-insensitive occurrence of a literal in a byte span.
Definition reflow_svg.c:166
static uint32_t internal_named_color(const uint8_t *s, size_t len)
Map a named SVG colour keyword to its 0x00RRGGBB value.
Definition reflow_svg.c:561
int32_t priv_match_grad(const svg_grads_t *grads, const uint8_t *val, size_t vlen)
Implementation of priv_match_grad() – linear id search over the document gradient set.
Definition reflow_svg.c:665
bool priv_ra8_svgp_starts_ci(const uint8_t *s, size_t len, size_t at, const char *lit)
Test whether a byte span starts with a literal at a given offset, case-insensitively.
Definition reflow_svg.c:123
int32_t priv_ra8_svgp_attr_num(const uint8_t *s, size_t len, const char *name, int32_t def)
Read an SVG attribute as a signed integer, returning a default when absent.
Definition reflow_svg.c:424
char priv_ra8_svgp_lc(char c)
ASCII-fold a character to lower case.
Definition reflow_svg.c:88
static bool internal_ci_eq(const uint8_t *s, size_t len, const char *lit)
Case-insensitive equality test between a byte span and a NUL-terminated literal.
Definition reflow_svg.c:464
static uint32_t internal_hex_color(const uint8_t *s, size_t len)
Parse a hex colour from raw digit bytes (no leading '#') into 0x00RRGGBB.
Definition reflow_svg.c:505
Minimal SVG handling for the reflow ereader (#112).
Cross-TU surface for the split minimal-SVG subset (#112).
struct svg_grads svg_grads_t
Forward decl: the document's gradient set (defined after svg_xform_t).
@ k_svg_hex6
#rrggbb digit count.
@ k_svg_hex_a10
Value of hex 'a'/'A'.
@ k_svg_hex3
#rgb digit count.
@ k_svg_hex_chan
Bits per colour channel.
@ k_svg_dec
Decimal base.
@ k_svg_no_paint
none / absent paint sentinel.
@ k_svg_hex_base
Hex base / "not a hex digit".
@ k_svg_hex_nib
Bits per hex nibble.
char id[k_svg_grad_id]
NUL-terminated gradient id (for url()).
svg_grad_t g[k_svg_grad_max]
The gradient table.
int32_t n
Number of valid gradients (<= k_svg_grad_max).
SVG-user-space -> framebuffer-box coordinate transform.
const svg_grads_t * grads
Document gradient set, or NULL.