ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_svg_xform.c
Go to the documentation of this file.
1
20
21#include <math.h>
22
23#include "ra8_attributes.h"
24#include "reflow_svg_internal.h"
25
26/* ===========================================================================
27 * Coordinate transform + affine `transform=` parsing
28 * ===========================================================================
29 */
30
57static int32_t internal_ux(const svg_xform_t* t, int32_t sx)
58{
59 return (int32_t)(((float)sx * t->ua) + t->ue);
60}
61
88static int32_t internal_uy(const svg_xform_t* t, int32_t sy)
89{
90 return (int32_t)(((float)sy * t->ud) + t->uf);
91}
92
119{
120 return (t->ub != 0.0F) || (t->uc != 0.0F);
121}
122
148void priv_ra8_svgp_map_point(const svg_xform_t* t, int32_t ux, int32_t uy, int32_t* fx, int32_t* fy)
149{
150 const int32_t px = (int32_t)(((float)ux * t->ua) + ((float)uy * t->uc) + t->ue);
151 const int32_t py = (int32_t)(((float)ux * t->ub) + ((float)uy * t->ud) + t->uf);
152 *fx = t->bx + (int32_t)(((int64_t)(px - t->vx) * (int64_t)t->bw) / (int64_t)t->vw);
153 *fy = t->by + (int32_t)(((int64_t)(py - t->vy) * (int64_t)t->bh) / (int64_t)t->vh);
154}
155
182int32_t priv_ra8_svgp_mx(const svg_xform_t* t, int32_t sx)
183{
184 const int32_t ux = internal_ux(t, sx);
185 return t->bx + (int32_t)(((int64_t)(ux - t->vx) * (int64_t)t->bw) / (int64_t)t->vw);
186}
187
214int32_t priv_ra8_svgp_my(const svg_xform_t* t, int32_t sy)
215{
216 const int32_t uy = internal_uy(t, sy);
217 return t->by + (int32_t)(((int64_t)(uy - t->vy) * (int64_t)t->bh) / (int64_t)t->vh);
218}
219
245int32_t priv_ra8_svgp_sx(const svg_xform_t* t, int32_t sw)
246{
247 const int32_t uw = (int32_t)((float)sw * t->ua);
248 return (int32_t)(((int64_t)uw * (int64_t)t->bw) / (int64_t)t->vw);
249}
250
280float priv_ra8_svgp_numf(const uint8_t* s, size_t len, size_t* i)
281{
282 while ((*i < len) && (priv_ra8_svgp_ws((char)s[*i]) || (s[*i] == (uint8_t)','))) {
283 ++(*i);
284 }
285 float sgn = 1.0F;
286 if ((*i < len) && ((s[*i] == (uint8_t)'-') || (s[*i] == (uint8_t)'+'))) {
287 sgn = (s[*i] == (uint8_t)'-') ? -1.0F : 1.0F;
288 ++(*i);
289 }
290 float v = 0.0F;
291 while ((*i < len) && (s[*i] >= (uint8_t)'0') && (s[*i] <= (uint8_t)'9')) {
292 v = (v * (float)k_svg_dec) + ((float)s[*i] - (float)'0');
293 ++(*i);
294 }
295 if ((*i < len) && (s[*i] == (uint8_t)'.')) {
296 ++(*i);
297 float frac = 1.0F;
298 while ((*i < len) && (s[*i] >= (uint8_t)'0') && (s[*i] <= (uint8_t)'9')) {
299 frac /= (float)k_svg_dec;
300 v += frac * ((float)s[*i] - (float)'0');
301 ++(*i);
302 }
303 }
304 return v * sgn;
305}
306
332{
333 svg_utf_t id = {.a = 1.0F, .b = 0.0F, .c = 0.0F, .d = 1.0F, .e = 0.0F, .f = 0.0F};
334 return id;
335}
336
366{
367 svg_utf_t r = {.a = (a.a * b.a) + (a.c * b.b),
368 .b = (a.b * b.a) + (a.d * b.b),
369 .c = (a.a * b.c) + (a.c * b.d),
370 .d = (a.b * b.c) + (a.d * b.d),
371 .e = (a.a * b.e) + (a.c * b.f) + a.e,
372 .f = (a.b * b.e) + (a.d * b.f) + a.f};
373 return r;
374}
375
405static bool internal_is_num_start(const uint8_t* s, size_t len, size_t at)
406{
407 if (at >= len) {
408 return false;
409 }
410 const char c = (char)s[at];
411 return ((c >= '0') && (c <= '9')) || (c == '-') || (c == '+') || (c == '.');
412}
413
427
440
465static float internal_deg2rad(float deg)
466{
467 static const float k_pi = 3.14159265F;
468 static const float k_deg_half = 180.0F;
469 return deg * (k_pi / k_deg_half);
470}
471
502static int32_t internal_xform_read(const uint8_t* v, size_t vlen, size_t* j, float* args)
503{
504 int32_t na = 0;
505 /* Bounded: <= k_svg_argc_cube args; each priv_ra8_svgp_numf advances *j. */
506 while (na < (int32_t)k_svg_argc_cube) {
507 while ((*j < vlen) && (priv_ra8_svgp_ws((char)v[*j]) || (v[*j] == (uint8_t)','))) {
508 ++(*j);
509 }
510 if (!internal_is_num_start(v, vlen, *j)) {
511 break;
512 }
513 args[na] = priv_ra8_svgp_numf(v, vlen, j);
514 ++na;
515 }
516 return na;
517}
518
546static svg_utf_t internal_xform_rotate(const float* args, int32_t na)
547{
548 const float co = cosf(internal_deg2rad(args[0]));
549 const float si = sinf(internal_deg2rad(args[0]));
550 const float cx = (na >= 3) ? args[1] : 0.0F;
551 const float cy = (na >= 3) ? args[2] : 0.0F;
552 return (svg_utf_t){.a = co,
553 .b = si,
554 .c = -si,
555 .d = co,
556 .e = (cx * (1.0F - co)) + (cy * si),
557 .f = (cy * (1.0F - co)) - (cx * si)};
558}
559
588static svg_utf_t internal_xform_build(svg_xf_kind_t kind, const float* args, int32_t na)
589{
591 switch (kind) {
593 result = (svg_utf_t){.a = 1.0F,
594 .b = 0.0F,
595 .c = 0.0F,
596 .d = 1.0F,
597 .e = args[0],
598 .f = (na >= 2) ? args[1] : 0.0F};
599 break;
600 case k_svg_xf_scale:
601 result = (svg_utf_t){.a = args[0],
602 .b = 0.0F,
603 .c = 0.0F,
604 .d = (na >= 2) ? args[1] : args[0],
605 .e = 0.0F,
606 .f = 0.0F};
607 break;
608 case k_svg_xf_rotate:
609 result = internal_xform_rotate(args, na);
610 break;
611 case k_svg_xf_skewx:
612 result = (svg_utf_t){.a = 1.0F,
613 .b = 0.0F,
614 .c = tanf(internal_deg2rad(args[0])),
615 .d = 1.0F,
616 .e = 0.0F,
617 .f = 0.0F};
618 break;
619 case k_svg_xf_skewy:
620 result = (svg_utf_t){.a = 1.0F,
621 .b = tanf(internal_deg2rad(args[0])),
622 .c = 0.0F,
623 .d = 1.0F,
624 .e = 0.0F,
625 .f = 0.0F};
626 break;
627 case k_svg_xf_matrix:
628 result = (svg_utf_t){.a = args[k_svg_xf_arg_a],
629 .b = args[k_svg_xf_arg_b],
630 .c = args[k_svg_xf_arg_c],
631 .d = args[k_svg_xf_arg_d],
632 .e = args[k_svg_xf_arg_e],
633 .f = args[k_svg_xf_arg_f]};
634 break;
635 case k_svg_xf_none:
636 default:
637 break;
638 }
639 return result;
640}
641
676static svg_xf_kind_t internal_xform_kind(const uint8_t* v, size_t vlen, size_t i)
677{
678 if (priv_ra8_svgp_starts_ci(v, vlen, i, "translate(")) {
679 return k_svg_xf_translate;
680 }
681 if (priv_ra8_svgp_starts_ci(v, vlen, i, "scale(")) {
682 return k_svg_xf_scale;
683 }
684 if (priv_ra8_svgp_starts_ci(v, vlen, i, "rotate(")) {
685 return k_svg_xf_rotate;
686 }
687 if (priv_ra8_svgp_starts_ci(v, vlen, i, "skewx(")) {
688 return k_svg_xf_skewx;
689 }
690 if (priv_ra8_svgp_starts_ci(v, vlen, i, "skewy(")) {
691 return k_svg_xf_skewy;
692 }
693 if (priv_ra8_svgp_starts_ci(v, vlen, i, "matrix(")) {
694 return k_svg_xf_matrix;
695 }
696 return k_svg_xf_none;
697}
698
728static svg_utf_t internal_parse_xform(const uint8_t* v, size_t vlen)
729{
731 size_t i = 0U;
732 /* Bounded: each pass consumes one `name(...)` group or breaks; <= vlen steps. */
733 while (i < vlen) {
734 while ((i < vlen) && (priv_ra8_svgp_ws((char)v[i]) || (v[i] == (uint8_t)','))) {
735 ++i;
736 }
737 const svg_xf_kind_t kind = internal_xform_kind(v, vlen, i);
738 size_t op = i;
739 while ((op < vlen) && (v[op] != (uint8_t)'(')) {
740 ++op;
741 }
742 if (op >= vlen) {
743 break;
744 }
745 size_t j = op + 1U;
746 if (kind != k_svg_xf_none) {
747 float args[k_svg_path_args] = {};
748 const int32_t na = internal_xform_read(v, vlen, &j, args);
749 acc = internal_utf_compose(acc, internal_xform_build(kind, args, na));
750 }
751 while ((j < vlen) && (v[j] != (uint8_t)')')) {
752 ++j;
753 }
754 i = (j < vlen) ? (j + 1U) : vlen;
755 }
756 return acc;
757}
758
785void priv_ra8_svgp_apply_xform(svg_xform_t* t, const uint8_t* tag, size_t tlen)
786{
787 size_t off = 0U;
788 size_t vl = 0U;
789 if (!priv_ra8_svgp_attr(tag, tlen, "transform", &off, &vl)) {
790 return;
791 }
792 const svg_utf_t cur = {.a = t->ua, .b = t->ub, .c = t->uc, .d = t->ud, .e = t->ue, .f = t->uf};
793 const svg_utf_t out = internal_utf_compose(cur, internal_parse_xform(&tag[off], vl));
794 t->ua = out.a;
795 t->ub = out.b;
796 t->uc = out.c;
797 t->ud = out.d;
798 t->ue = out.e;
799 t->uf = out.f;
800}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
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
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
Cross-TU surface for the split minimal-SVG subset (#112).
@ k_svg_path_args
Max args of a path command (A).
@ k_svg_dec
Decimal base.
@ k_svg_argc_cube
Arg count of C.
int32_t priv_ra8_svgp_sx(const svg_xform_t *t, int32_t sw)
Scale a user-space length by the user x scale and the viewBox x ratio.
static svg_utf_t internal_xform_build(svg_xf_kind_t kind, const float *args, int32_t na)
Build the 2x3 affine for one parsed SVG transform function.
static int32_t internal_ux(const svg_xform_t *t, int32_t sx)
Apply the axis-aligned user transform (a/translate) to a user-space x.
static svg_utf_t internal_xform_rotate(const float *args, int32_t na)
Build the 2x3 affine for an SVG 'rotate(deg[,cx,cy])' transform.
void priv_ra8_svgp_map_point(const svg_xform_t *t, int32_t ux, int32_t uy, int32_t *fx, int32_t *fy)
Map a user-space point through the FULL affine, then viewBox->box.
float priv_ra8_svgp_numf(const uint8_t *s, size_t len, size_t *i)
Parse one SVG floating-point number at s[*i], skipping leading separators.
svg_xf_arg_t
Argument indices into a parsed matrix(a,b,c,d,e,f) transform list.
@ k_svg_xf_arg_a
matrix component a (x scale).
@ k_svg_xf_arg_d
matrix component d (y scale).
@ k_svg_xf_arg_c
matrix component c (x shear).
@ k_svg_xf_arg_b
matrix component b (y shear).
@ k_svg_xf_arg_e
matrix component e (x translate).
@ k_svg_xf_arg_f
matrix component f (y translate).
static int32_t internal_xform_read(const uint8_t *v, size_t vlen, size_t *j, float *args)
Read up to 6 float arguments from a transform function's '(...)' list.
static float internal_deg2rad(float deg)
Convert an angle from degrees to radians.
svg_xf_kind_t
transform= function kinds recognised by internal_parse_xform.
@ k_svg_xf_matrix
matrix(a,b,c,d,e,f).
@ k_svg_xf_translate
translate(tx[,ty]).
@ k_svg_xf_scale
scale(sx[,sy]).
@ k_svg_xf_skewx
skewX(deg).
@ k_svg_xf_rotate
rotate(deg[,cx,cy]).
@ k_svg_xf_skewy
skewY(deg).
@ k_svg_xf_none
Unrecognised function -> skipped.
void priv_ra8_svgp_apply_xform(svg_xform_t *t, const uint8_t *tag, size_t tlen)
Compose the 'transform=' attribute from tag onto t's user affine.
int32_t priv_ra8_svgp_mx(const svg_xform_t *t, int32_t sx)
Map a user-space x coordinate to a framebuffer x coordinate.
static svg_utf_t internal_parse_xform(const uint8_t *v, size_t vlen)
Parse a 'transform=' attribute value into a composed 2x3 affine.
static bool internal_is_num_start(const uint8_t *s, size_t len, size_t at)
Test whether a byte position starts an SVG number literal.
static int32_t internal_uy(const svg_xform_t *t, int32_t sy)
Apply the axis-aligned user transform (d/translate) to a user-space y.
static svg_utf_t internal_utf_identity(void)
Return the 2x3 affine identity transform.
static svg_utf_t internal_utf_compose(svg_utf_t a, svg_utf_t b)
Compose two 2x3 affines: a (outer) over b (inner).
static svg_xf_kind_t internal_xform_kind(const uint8_t *v, size_t vlen, size_t i)
Identify the SVG transform function keyword starting at v[i].
bool priv_ra8_svgp_has_rot(const svg_xform_t *t)
Test whether the user transform contains rotation or shear.
int32_t priv_ra8_svgp_my(const svg_xform_t *t, int32_t sy)
Map a user-space y coordinate to a framebuffer y coordinate.
A user transform= as a full 2x3 affine matrix.
float c
Row0 col1 (x shear).
float e
X translate.
float f
Y translate.
float a
Row0 col0 (x scale).
float b
Row1 col0 (y shear).
float d
Row1 col1 (y scale).
SVG-user-space -> framebuffer-box coordinate transform.
float ub
Affine b (y shear; 0 = axis-aligned).
int32_t bw
Box width.
int32_t vx
viewBox min-x.
float ue
Affine e (x translate, user units).
float ud
Affine d (y scale; 1 = identity).
int32_t bx
Box left.
int32_t vy
viewBox min-y.
int32_t vw
viewBox width.
float uf
Affine f (y translate, user units).
int32_t vh
viewBox height.
int32_t by
Box top.
float uc
Affine c (x shear; 0 = axis-aligned).
float ua
Affine a (x scale; 1 = identity).
int32_t bh
Box height.