ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
reflow_svg_shape.c
Go to the documentation of this file.
1
21
22#include <math.h>
23
24#include "ra8_attributes.h"
25#include "ra8_gfx.h"
26#include "reflow_svg_internal.h"
27
29static const float s_svg_2pi = 6.28318531F;
30
31/* ===========================================================================
32 * Basic shapes (rect / circle / line)
33 * ===========================================================================
34 */
35
61void priv_ra8_svgp_draw_rect(const uint8_t* s, size_t len, const svg_xform_t* t)
62{
63 int32_t gi = -1;
64 const uint32_t fill = priv_ra8_svgp_resolve_fill(s, len, t, (uint32_t)k_svg_def_fill, &gi);
65 if ((gi < 0) && (fill == (uint32_t)k_svg_no_paint)) {
66 return;
67 }
68 const int32_t rx = priv_ra8_svgp_attr_num(s, len, "x", 0);
69 const int32_t ry = priv_ra8_svgp_attr_num(s, len, "y", 0);
70 const int32_t rw = priv_ra8_svgp_attr_num(s, len, "width", 0);
71 const int32_t rh = priv_ra8_svgp_attr_num(s, len, "height", 0);
72 if ((gi < 0) && !priv_ra8_svgp_has_rot(t)) {
73 (void)ra8_gfx_rect(priv_ra8_svgp_mx(t, rx),
74 priv_ra8_svgp_my(t, ry),
75 priv_ra8_svgp_sx(t, rw),
76 priv_ra8_svgp_sx(t, rh),
77 fill,
78 true);
79 return;
80 }
81 int32_t xs[k_svg_rect_pts] = {};
82 int32_t ys[k_svg_rect_pts] = {};
83 priv_ra8_svgp_map_point(t, rx, ry, &xs[0], &ys[0]);
84 priv_ra8_svgp_map_point(t, rx + rw, ry, &xs[1], &ys[1]);
85 priv_ra8_svgp_map_point(t, rx + rw, ry + rh, &xs[2], &ys[2]);
86 priv_ra8_svgp_map_point(t, rx, ry + rh, &xs[3], &ys[3]);
87 if (gi >= 0) {
88 priv_ra8_svgp_fill_poly_grad(xs, ys, (int32_t)k_svg_rect_pts, &t->grads->g[gi]);
89 } else {
90 priv_ra8_svgp_fill_poly(xs, ys, (int32_t)k_svg_rect_pts, fill);
91 }
92}
93
119void priv_ra8_svgp_draw_circle(const uint8_t* s, size_t len, const svg_xform_t* t)
120{
121 int32_t gi = -1;
122 const uint32_t fill = priv_ra8_svgp_resolve_fill(s, len, t, (uint32_t)k_svg_def_fill, &gi);
123 if ((gi < 0) && (fill == (uint32_t)k_svg_no_paint)) {
124 return;
125 }
126 const int32_t cx = priv_ra8_svgp_attr_num(s, len, "cx", 0);
127 const int32_t cy = priv_ra8_svgp_attr_num(s, len, "cy", 0);
128 const int32_t r = priv_ra8_svgp_attr_num(s, len, "r", 0);
129 if ((gi < 0) && !priv_ra8_svgp_has_rot(t)) {
130 (void)ra8_gfx_circle(priv_ra8_svgp_mx(t, cx),
131 priv_ra8_svgp_my(t, cy),
132 priv_ra8_svgp_sx(t, r),
133 fill,
134 true);
135 return;
136 }
137 int32_t xs[k_svg_circle_seg] = {};
138 int32_t ys[k_svg_circle_seg] = {};
139 /* Bounded: k_svg_circle_seg vertices around the circle. */
140 for (int32_t k = 0; k < (int32_t)k_svg_circle_seg; ++k) {
141 const float ang = (s_svg_2pi * (float)k) / (float)k_svg_circle_seg;
142 const float dx = (float)r * cosf(ang);
143 const float dy = (float)r * sinf(ang);
144 priv_ra8_svgp_map_point(t, cx + (int32_t)dx, cy + (int32_t)dy, &xs[k], &ys[k]);
145 }
146 if (gi >= 0) {
147 priv_ra8_svgp_fill_poly_grad(xs, ys, (int32_t)k_svg_circle_seg, &t->grads->g[gi]);
148 } else {
149 priv_ra8_svgp_fill_poly(xs, ys, (int32_t)k_svg_circle_seg, fill);
150 }
151}
152
177void priv_ra8_svgp_draw_line(const uint8_t* s, size_t len, const svg_xform_t* t)
178{
179 const uint32_t stroke = priv_ra8_svgp_attr_paint(s, len, "stroke", (uint32_t)k_svg_no_paint);
180 if (stroke == (uint32_t)k_svg_no_paint) {
181 return;
182 }
183 const int32_t x1 = priv_ra8_svgp_attr_num(s, len, "x1", 0);
184 const int32_t y1 = priv_ra8_svgp_attr_num(s, len, "y1", 0);
185 const int32_t x2 = priv_ra8_svgp_attr_num(s, len, "x2", 0);
186 const int32_t y2 = priv_ra8_svgp_attr_num(s, len, "y2", 0);
187 int32_t fx1 = 0;
188 int32_t fy1 = 0;
189 int32_t fx2 = 0;
190 int32_t fy2 = 0;
191 priv_ra8_svgp_map_point(t, x1, y1, &fx1, &fy1);
192 priv_ra8_svgp_map_point(t, x2, y2, &fx2, &fy2);
193 (void)ra8_gfx_line(fx1, fy1, fx2, fy2, stroke);
194}
195
196/* ===========================================================================
197 * Polygon / polyline (a `points` list + a scanline polygon fill)
198 * ===========================================================================
199 */
200
232static int32_t
233internal_parse_points(const uint8_t* v, size_t vlen, const svg_xform_t* t, int32_t* xs, int32_t* ys)
234{
235 size_t k = 0U;
236 int32_t n = 0;
237 /* Bounded: <= k_svg_poly_max pairs; k advances past each number. */
238 while (n < (int32_t)k_svg_poly_max) {
239 while ((k < vlen) && (priv_ra8_svgp_ws((char)v[k]) || (v[k] == (uint8_t)','))) {
240 ++k;
241 }
242 if (k >= vlen) {
243 break;
244 }
245 const int32_t ux = priv_ra8_svgp_num(v, vlen, &k);
246 const int32_t uy = priv_ra8_svgp_num(v, vlen, &k);
247 priv_ra8_svgp_map_point(t, ux, uy, &xs[n], &ys[n]);
248 ++n;
249 }
250 return n;
251}
252
277static void internal_sort_i32(int32_t* a, int32_t m)
278{
279 /* Bounded: m <= k_svg_poly_max. */
280 for (int32_t i = 1; i < m; ++i) {
281 const int32_t key = a[i];
282 int32_t j = i - 1;
283 while ((j >= 0) && (a[j] > key)) {
284 a[j + 1] = a[j];
285 --j;
286 }
287 a[j + 1] = key;
288 }
289}
290
322static int32_t
323internal_scanline_x(const int32_t* xs, const int32_t* ys, int32_t n, int32_t y, int32_t* xint)
324{
325 int32_t m = 0;
326 /* Bounded: one test per polygon edge (n edges). */
327 /* mcdc-deactivated: the polygon point count n is itself capped at k_svg_poly_max by the point parser, so the crossing count m (<= edges processed <= i < n <= k_svg_poly_max) can never reach k_svg_poly_max before i exhausts n; (m < k_svg_poly_max) is invariantly true and its false arm is unreachable. */
328 for (int32_t i = 0; (i < n) && (m < (int32_t)k_svg_poly_max); ++i) {
329 const int32_t j = (i + 1) % n;
330 const int32_t y0 = ys[i];
331 const int32_t y1 = ys[j];
332 /* mcdc-deactivated: the fourth condition (y < y0) is the exact boolean negation of the first (y0 <= y), so it cannot be flipped independently of C1; the C4 independence pair is structurally unreachable. */
333 if (((y0 <= y) && (y < y1)) || ((y1 <= y) && (y < y0))) {
334 const int32_t x0 = xs[i];
335 const int32_t x1 = xs[j];
336 const int64_t y_delta = (int64_t)y - (int64_t)y0;
337 const int64_t x_delta = (int64_t)x1 - (int64_t)x0;
338 const int64_t divisor = (int64_t)y1 - (int64_t)y0;
339 const int64_t offset = (y_delta * x_delta) / divisor;
340 xint[m] = x0 + (int32_t)offset;
341 ++m;
342 }
343 }
344 return m;
345}
346
371void priv_ra8_svgp_fill_poly(const int32_t* xs, const int32_t* ys, int32_t n, uint32_t color)
372{
373 if (n < (int32_t)k_svg_poly_min) {
374 return;
375 }
376 int32_t ymin = ys[0];
377 int32_t ymax = ys[0];
378 for (int32_t i = 1; i < n; ++i) {
379 ymin = (ys[i] < ymin) ? ys[i] : ymin;
380 ymax = (ys[i] > ymax) ? ys[i] : ymax;
381 }
382 /* Bounded: ymax - ymin <= the framebuffer-box height. */
383 for (int32_t y = ymin; y <= ymax; ++y) {
384 int32_t xint[k_svg_poly_max] = {};
385 const int32_t m = internal_scanline_x(xs, ys, n, y, xint);
386 internal_sort_i32(xint, m);
387 for (int32_t k = 0; (k + 1) < m; k += 2) {
388 (void)ra8_gfx_line(xint[k], y, xint[k + 1], y, color);
389 }
390 }
391}
392
421static uint32_t internal_col_lerp(uint32_t a, uint32_t b, float f)
422{
423 const uint32_t ra_bits = (a >> (uint32_t)k_svg_sh_r) & (uint32_t)k_svg_chan_mask;
424 const uint32_t ga_bits = (a >> (uint32_t)k_svg_hex_chan) & (uint32_t)k_svg_chan_mask;
425 const uint32_t ba_bits = a & (uint32_t)k_svg_chan_mask;
426 const uint32_t rb_bits = (b >> (uint32_t)k_svg_sh_r) & (uint32_t)k_svg_chan_mask;
427 const uint32_t gb_bits = (b >> (uint32_t)k_svg_hex_chan) & (uint32_t)k_svg_chan_mask;
428 const uint32_t bb_bits = b & (uint32_t)k_svg_chan_mask;
429 const int32_t ra = (int32_t)ra_bits;
430 const int32_t ga = (int32_t)ga_bits;
431 const int32_t ba = (int32_t)ba_bits;
432 const int32_t rb = (int32_t)rb_bits;
433 const int32_t gb = (int32_t)gb_bits;
434 const int32_t bb = (int32_t)bb_bits;
435 const int32_t rd = rb - ra;
436 const int32_t gd = gb - ga;
437 const int32_t bd = bb - ba;
438 const int32_t r = ra + (int32_t)((float)rd * f);
439 const int32_t g = ga + (int32_t)((float)gd * f);
440 const int32_t bl = ba + (int32_t)((float)bd * f);
441 return ((uint32_t)r << (uint32_t)k_svg_sh_r) | ((uint32_t)g << (uint32_t)k_svg_hex_chan) |
442 (uint32_t)bl;
443}
444
477static uint32_t internal_grad_eval(const svg_grad_t* g, float px, float py)
478{
479 if (g->nstops == 0U) {
480 return (uint32_t)k_svg_def_fill;
481 }
482 float p = 0.0F;
483 if (g->kind == (uint8_t)k_svg_grad_radial) {
484 const float dx = px - g->x1;
485 const float dy = py - g->y1;
486 const float rr = (g->x2 > 0.0F) ? g->x2 : 1.0F;
487 p = sqrtf((dx * dx) + (dy * dy)) / rr;
488 } else {
489 const float vx = g->x2 - g->x1;
490 const float vy = g->y2 - g->y1;
491 const float len2 = (vx * vx) + (vy * vy);
492 p = (len2 > 0.0F) ? ((((px - g->x1) * vx) + ((py - g->y1) * vy)) / len2) : 0.0F;
493 }
494 if (p <= g->stops[0].off) {
495 return g->stops[0].col;
496 }
497 const uint8_t last = (uint8_t)(g->nstops - 1U);
498 if (p >= g->stops[last].off) {
499 return g->stops[last].col;
500 }
501 /* Bounded: at most nstops-1 stop brackets. */
502 for (uint8_t i = 0U; i < last; ++i) {
503 const float o0 = g->stops[i].off;
504 const float o1 = g->stops[i + 1U].off;
505 /* mcdc-deactivated: the loop is entered only when p > stops[0].off, and reaching bracket i without returning requires p to exceed every earlier stop's upper offset, so p >= o0 (== stops[i].off) invariantly holds regardless of stop ordering; (p >= o0) cannot be flipped false. */
506 if ((p >= o0) && (p <= o1)) {
507 const float f = (o1 > o0) ? ((p - o0) / (o1 - o0)) : 0.0F;
508 return internal_col_lerp(g->stops[i].col, g->stops[i + 1U].col, f);
509 }
510 }
511 return g->stops[last].col;
512}
513
541void priv_ra8_svgp_fill_poly_grad(const int32_t* xs,
542 const int32_t* ys,
543 int32_t n,
544 const svg_grad_t* g)
545{
546 if (n < (int32_t)k_svg_poly_min) {
547 return;
548 }
549 int32_t xmin = xs[0];
550 int32_t xmax = xs[0];
551 int32_t ymin = ys[0];
552 int32_t ymax = ys[0];
553 for (int32_t i = 1; i < n; ++i) {
554 xmin = (xs[i] < xmin) ? xs[i] : xmin;
555 xmax = (xs[i] > xmax) ? xs[i] : xmax;
556 ymin = (ys[i] < ymin) ? ys[i] : ymin;
557 ymax = (ys[i] > ymax) ? ys[i] : ymax;
558 }
559 const float bw = (xmax > xmin) ? (float)((int64_t)xmax - (int64_t)xmin) : 1.0F;
560 const float bh = (ymax > ymin) ? (float)((int64_t)ymax - (int64_t)ymin) : 1.0F;
561 /* Bounded: ymax - ymin <= the framebuffer-box height. */
562 for (int32_t y = ymin; y <= ymax; ++y) {
563 int32_t xint[k_svg_poly_max] = {};
564 const int32_t m = internal_scanline_x(xs, ys, n, y, xint);
565 internal_sort_i32(xint, m);
566 const float py = (float)((int64_t)y - (int64_t)ymin) / bh;
567 for (int32_t k = 0; (k + 1) < m; k += 2) {
568 /* Bounded: xint[k+1] - xint[k] <= the framebuffer-box width. */
569 for (int32_t x = xint[k]; x <= xint[k + 1]; ++x) {
570 const float px = (float)((int64_t)x - (int64_t)xmin) / bw;
571 (void)ra8_gfx_pixel(x, y, internal_grad_eval(g, px, py));
572 }
573 }
574 }
575}
576
601void priv_ra8_svgp_draw_polygon(const uint8_t* s, size_t len, const svg_xform_t* t)
602{
603 int32_t gi = -1;
604 const uint32_t fill = priv_ra8_svgp_resolve_fill(s, len, t, (uint32_t)k_svg_def_fill, &gi);
605 size_t off = 0U;
606 size_t vl = 0U;
607 if (((gi < 0) && (fill == (uint32_t)k_svg_no_paint)) ||
608 !priv_ra8_svgp_attr(s, len, "points", &off, &vl)) {
609 return;
610 }
611 int32_t xs[k_svg_poly_max] = {};
612 int32_t ys[k_svg_poly_max] = {};
613 const int32_t n = internal_parse_points(&s[off], vl, t, xs, ys);
614 if (gi >= 0) {
615 priv_ra8_svgp_fill_poly_grad(xs, ys, n, &t->grads->g[gi]);
616 } else {
617 priv_ra8_svgp_fill_poly(xs, ys, n, fill);
618 }
619}
620
644void priv_ra8_svgp_draw_polyline(const uint8_t* s, size_t len, const svg_xform_t* t)
645{
646 const uint32_t stroke = priv_ra8_svgp_attr_paint(s, len, "stroke", (uint32_t)k_svg_no_paint);
647 size_t off = 0U;
648 size_t vl = 0U;
649 if ((stroke == (uint32_t)k_svg_no_paint) || !priv_ra8_svgp_attr(s, len, "points", &off, &vl)) {
650 return;
651 }
652 int32_t xs[k_svg_poly_max] = {};
653 int32_t ys[k_svg_poly_max] = {};
654 const int32_t n = internal_parse_points(&s[off], vl, t, xs, ys);
655 /* Bounded: n - 1 segments. */
656 for (int32_t i = 0; (i + 1) < n; ++i) {
657 (void)ra8_gfx_line(xs[i], ys[i], xs[i + 1], ys[i + 1], stroke);
658 }
659}
660
686static float internal_absf(float v)
687{
688 return (v < 0.0F) ? -v : v;
689}
690
700typedef struct {
701 float cx;
702 float cy;
703 float rx;
704 float ry;
705 float cos_phi;
706 float sin_phi;
707 float t1;
708 float dt;
709 bool ok;
710} svg_arc_t;
711
745 float x1p,
746 float y1p,
747 float rx,
748 float ry,
749 float mx,
750 float my,
751 bool large,
752 bool sweep)
753{
754 const float rx2 = rx * rx;
755 const float ry2 = ry * ry;
756 const float num = (rx2 * ry2) - (rx2 * y1p * y1p) - (ry2 * x1p * x1p);
757 const float den = (rx2 * y1p * y1p) + (ry2 * x1p * x1p);
758 float co = sqrtf((num > 0.0F) ? (num / den) : 0.0F);
759 if (large == sweep) {
760 co = -co;
761 }
762 const float cxp = co * ((rx * y1p) / ry);
763 const float cyp = -co * ((ry * x1p) / rx);
764 a->cx = (a->cos_phi * cxp) - (a->sin_phi * cyp) + mx;
765 a->cy = (a->sin_phi * cxp) + (a->cos_phi * cyp) + my;
766 a->t1 = atan2f((y1p - cyp) / ry, (x1p - cxp) / rx);
767 float dt = atan2f((-y1p - cyp) / ry, (-x1p - cxp) / rx) - a->t1;
768 if (!sweep && (dt > 0.0F)) {
769 dt -= s_svg_2pi;
770 } else if (sweep && (dt < 0.0F)) {
771 dt += s_svg_2pi;
772 } else {
773 /* The sweep already has the requested direction. */
774 }
775 a->dt = dt;
776 a->rx = rx;
777 a->ry = ry;
778 a->ok = true;
779}
780
817 svg_pt_t p_end,
818 int32_t rx_in,
819 int32_t ry_in,
820 int32_t rot_deg,
821 bool large,
822 bool sweep)
823{
824 static const float k_pi = 3.14159265F;
825 static const float k_deg_half = 180.0F;
826 static const float k_half = 0.5F;
827 svg_arc_t a = {};
828 float rx = internal_absf((float)rx_in);
829 float ry = internal_absf((float)ry_in);
830 if ((rx == 0.0F) || (ry == 0.0F) || ((p0.x == p_end.x) && (p0.y == p_end.y))) {
831 a.ok = false;
832 return a;
833 }
834 const float phi = (float)rot_deg * (k_pi / k_deg_half);
835 a.cos_phi = cosf(phi);
836 a.sin_phi = sinf(phi);
837 const float dx = (float)((int64_t)p0.x - (int64_t)p_end.x) * k_half;
838 const float dy = (float)((int64_t)p0.y - (int64_t)p_end.y) * k_half;
839 const float x1p = (a.cos_phi * dx) + (a.sin_phi * dy);
840 const float y1p = -(a.sin_phi * dx) + (a.cos_phi * dy);
841 const float lam = ((x1p * x1p) / (rx * rx)) + ((y1p * y1p) / (ry * ry));
842 if (lam > 1.0F) {
843 const float s = sqrtf(lam);
844 rx *= s;
845 ry *= s;
846 }
847 const float mx = (float)((int64_t)p0.x + (int64_t)p_end.x) * k_half;
848 const float my = (float)((int64_t)p0.y + (int64_t)p_end.y) * k_half;
849 internal_arc_solve(&a, x1p, y1p, rx, ry, mx, my, large, sweep);
850 return a;
851}
852
878static int32_t internal_arc_segs(float dt)
879{
880 static const float k_arc_step = 0.39269908F;
881 int32_t segs = (int32_t)(internal_absf(dt) / k_arc_step) + 1;
882 if (segs > (int32_t)k_svg_arc_seg_max) {
883 segs = (int32_t)k_svg_arc_seg_max;
884 }
885 return segs;
886}
887
918 svg_pt_t p0,
919 const int32_t* args,
920 svg_pt_t p_end,
921 int32_t* xs,
922 int32_t* ys,
923 int32_t* n)
924{
925 const svg_arc_t a = internal_arc_center(p0,
926 p_end,
927 args[k_svg_arc_rx],
928 args[k_svg_arc_ry],
929 args[k_svg_arc_rot],
930 args[k_svg_arc_large] != 0,
931 args[k_svg_arc_sweep] != 0);
932 if (!a.ok) {
933 if (*n < (int32_t)k_svg_poly_max) {
934 priv_ra8_svgp_map_point(t, p_end.x, p_end.y, &xs[*n], &ys[*n]);
935 ++(*n);
936 }
937 return;
938 }
939 const int32_t segs = internal_arc_segs(a.dt);
940 /* Bounded: segs (<= k_svg_arc_seg_max) samples, capped by k_svg_poly_max. */
941 for (int32_t j = 1; (j <= segs) && (*n < (int32_t)k_svg_poly_max); ++j) {
942 const float th = a.t1 + (a.dt * ((float)j / (float)segs));
943 const float ex = a.rx * cosf(th);
944 const float ey = a.ry * sinf(th);
945 const float px = a.cx + (a.cos_phi * ex) - (a.sin_phi * ey);
946 const float py = a.cy + (a.sin_phi * ex) + (a.cos_phi * ey);
947 priv_ra8_svgp_map_point(t, (int32_t)px, (int32_t)py, &xs[*n], &ys[*n]);
948 ++(*n);
949 }
950}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
ra8_err_t ra8_gfx_circle(int32_t cx, int32_t cy, int32_t r, uint32_t color, bool filled)
Draw a circle using midpoint algorithm.
ra8_err_t ra8_gfx_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
Draw a line from (x0,y0) to (x1,y1) with Bresenham's algorithm.
ra8_err_t ra8_gfx_pixel(int32_t x, int32_t y, uint32_t color)
Set a single pixel.
ra8_err_t ra8_gfx_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color, bool filled)
Draw an axis-aligned rectangle.
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
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
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
Cross-TU surface for the split minimal-SVG subset (#112).
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.
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.
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.
bool priv_ra8_svgp_has_rot(const svg_xform_t *t)
Test whether the user transform contains rotation or shear.
@ k_svg_grad_radial
<radialGradient> kind.
@ k_svg_def_fill
SVG default fill is black.
@ k_svg_arc_large
A arg index: large-arc flag.
@ k_svg_sh_r
Red shift within 0x00RRGGBB.
@ k_svg_arc_seg_max
Max segments per arc flatten.
@ k_svg_poly_min
Min points for a fillable polygon.
@ k_svg_arc_rx
A arg index: semi-axis X.
@ k_svg_hex_chan
Bits per colour channel.
@ k_svg_no_paint
none / absent paint sentinel.
@ k_svg_poly_max
Max points / scanline crossings.
@ k_svg_arc_ry
A arg index: semi-axis Y.
@ k_svg_arc_sweep
A arg index: sweep flag.
@ k_svg_rect_pts
Corner count of a rectangle.
@ k_svg_arc_rot
A arg index: x-axis rotation.
@ k_svg_circle_seg
N-gon segments for a transformed circle.
@ k_svg_chan_mask
One 8-bit RGB channel mask.
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.
static int32_t internal_parse_points(const uint8_t *v, size_t vlen, const svg_xform_t *t, int32_t *xs, int32_t *ys)
Parse a 'points' attribute value into framebuffer-space vertices.
static void internal_arc_solve(svg_arc_t *a, float x1p, float y1p, float rx, float ry, float mx, float my, bool large, bool sweep)
Solve an arc's centre + start/sweep angles (F.6.5.2 / F.6.5.5-6).
static void internal_sort_i32(int32_t *a, int32_t m)
Sort m int32 values in a into ascending order using insertion sort.
void priv_ra8_svgp_fill_poly_grad(const int32_t *xs, const int32_t *ys, int32_t n, const svg_grad_t *g)
Per-pixel gradient scanline fill of polygon (xs,ys)[0..n).
void priv_ra8_svgp_draw_circle(const uint8_t *s, size_t len, const svg_xform_t *t)
Draw one SVG 'circle' element using its fill colour or gradient.
void priv_ra8_svgp_draw_polygon(const uint8_t *s, size_t len, const svg_xform_t *t)
Draw one SVG 'polygon' element as a filled polygon.
void priv_ra8_svgp_fill_poly(const int32_t *xs, const int32_t *ys, int32_t n, uint32_t color)
Even-odd scanline fill of polygon (xs,ys)[0..n) with a solid colour.
void priv_ra8_svgp_draw_polyline(const uint8_t *s, size_t len, const svg_xform_t *t)
Draw one SVG 'polyline' element as connected stroke line segments.
static uint32_t internal_col_lerp(uint32_t a, uint32_t b, float f)
Linear-interpolate two 0x00RRGGBB colours by factor f in [0, 1].
void priv_ra8_svgp_draw_rect(const uint8_t *s, size_t len, const svg_xform_t *t)
Draw one SVG 'rect' element using its fill colour or gradient.
void priv_ra8_svgp_flatten_arc(const svg_xform_t *t, svg_pt_t p0, const int32_t *args, svg_pt_t p_end, int32_t *xs, int32_t *ys, int32_t *n)
Flatten an SVG elliptical arc into the polygon vertex array.
static int32_t internal_scanline_x(const int32_t *xs, const int32_t *ys, int32_t n, int32_t y, int32_t *xint)
Collect framebuffer X coordinates where polygon edges cross scanline y.
static int32_t internal_arc_segs(float dt)
Compute a bounded segment count for an arc of signed sweep dt.
void priv_ra8_svgp_draw_line(const uint8_t *s, size_t len, const svg_xform_t *t)
Draw one SVG 'line' element using its stroke colour.
static const float s_svg_2pi
2*Pi, for wrapping an arc's signed sweep into range.
static uint32_t internal_grad_eval(const svg_grad_t *g, float px, float py)
Evaluate gradient g at bbox-relative point (px, py).
static svg_arc_t internal_arc_center(svg_pt_t p0, svg_pt_t p_end, int32_t rx_in, int32_t ry_in, int32_t rot_deg, bool large, bool sweep)
Convert an SVG endpoint-parametrised arc to centre parametrisation.
static float internal_absf(float v)
Compute the absolute value of a float without using libm fabsf.
Centre-parametrisation of an SVG elliptical arc.
float cos_phi
cos(x-axis rotation).
float dt
Signed sweep, radians.
float sin_phi
sin(x-axis rotation).
float cx
Centre X (user space).
float rx
Corrected semi-axis X.
float t1
Start angle, radians.
bool ok
False => degenerate (draw a line).
float cy
Centre Y (user space).
float ry
Corrected semi-axis Y.
A parsed linear/radial gradient in objectBoundingBox units.
float x2
Linear end x / radial radius.
uint8_t kind
k_svg_grad_linear / k_svg_grad_radial.
float y1
Linear start y / radial centre y.
float y2
Linear end y (unused for radial).
uint8_t nstops
Number of valid stops.
float x1
Linear start x / radial centre x.
svg_stop_t stops[k_svg_grad_stops]
Colour stops, in document order.
svg_grad_t g[k_svg_grad_max]
The gradient table.
A 2-D point in SVG user space (an x/y pair).
int32_t y
Y coordinate.
int32_t x
X coordinate.
float off
Stop offset, clamped to [0,1].
uint32_t col
Stop colour, 0x00RRGGBB.
SVG-user-space -> framebuffer-box coordinate transform.
const svg_grads_t * grads
Document gradient set, or NULL.