fix: sync template sysroot, add script
This commit is contained in:
@@ -123,6 +123,34 @@ struct SvgCssTable {
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Integer sqrt (binary method, no floating point).
|
||||
// Returns floor(sqrt(n)) for any uint64.
|
||||
// ---------------------------------------------------------------------------
|
||||
inline uint64_t svg_int64_sqrt(uint64_t n) {
|
||||
if (n == 0) return 0;
|
||||
uint64_t r = 0;
|
||||
uint64_t bit = (uint64_t)1 << 62;
|
||||
while (bit > n) bit >>= 2;
|
||||
while (bit != 0) {
|
||||
if (n >= r + bit) {
|
||||
n -= r + bit;
|
||||
r = (r >> 1) + bit;
|
||||
} else {
|
||||
r >>= 1;
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// 16.16 fixed-point hypotenuse: sqrt(x*x + y*y).
|
||||
inline fixed_t svg_fixed_hypot(fixed_t x, fixed_t y) {
|
||||
int64_t sq = (int64_t)x * x + (int64_t)y * y;
|
||||
if (sq < 0) sq = 0;
|
||||
return (fixed_t)svg_int64_sqrt((uint64_t)sq);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixed-point number parser (NO floating point)
|
||||
// Parses strings like "3.25", "-0.5", ".1115", "16"
|
||||
@@ -566,6 +594,47 @@ inline void svg_path_to_edges(SvgEdgeList& el, const char* d, int dLen,
|
||||
*oy = fixed_mul(y - off_y, scale_y);
|
||||
};
|
||||
|
||||
// Emit a circular arc on (cx, cy, r) from p1 to p2 by recursive
|
||||
// chord-midpoint subdivision. Assumes the arc is <= 180deg; callers split
|
||||
// large arcs at the far point first. Stack-based to avoid recursive lambdas.
|
||||
auto emit_small_arc = [&](fixed_t cx, fixed_t cy, fixed_t r,
|
||||
fixed_t p1x, fixed_t p1y,
|
||||
fixed_t p2x, fixed_t p2y) {
|
||||
constexpr int ARC_MAX_DEPTH = 4; // up to 16 chords per sub-arc
|
||||
struct Seg { fixed_t ax, ay, bx, by; int depth; };
|
||||
Seg stk[16];
|
||||
int sp = 0;
|
||||
stk[sp++] = { p1x, p1y, p2x, p2y, ARC_MAX_DEPTH };
|
||||
while (sp > 0) {
|
||||
Seg s = stk[--sp];
|
||||
if (s.depth == 0) {
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(s.ax, s.ay, &sx0, &sy0);
|
||||
scale_pt(s.bx, s.by, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
continue;
|
||||
}
|
||||
fixed_t mx = (s.ax + s.bx) / 2;
|
||||
fixed_t my = (s.ay + s.by) / 2;
|
||||
fixed_t vx = mx - cx;
|
||||
fixed_t vy = my - cy;
|
||||
fixed_t vlen = svg_fixed_hypot(vx, vy);
|
||||
if (vlen == 0) {
|
||||
// Degenerate: chord midpoint coincides with center.
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(s.ax, s.ay, &sx0, &sy0);
|
||||
scale_pt(s.bx, s.by, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
continue;
|
||||
}
|
||||
fixed_t k = fixed_div(r, vlen);
|
||||
fixed_t amx = cx + fixed_mul(vx, k);
|
||||
fixed_t amy = cy + fixed_mul(vy, k);
|
||||
stk[sp++] = { amx, amy, s.bx, s.by, s.depth - 1 };
|
||||
stk[sp++] = { s.ax, s.ay, amx, amy, s.depth - 1 };
|
||||
}
|
||||
};
|
||||
|
||||
while (pp.has_more()) {
|
||||
char cmd = '\0';
|
||||
|
||||
@@ -773,23 +842,90 @@ inline void svg_path_to_edges(SvgEdgeList& el, const char* d, int dLen,
|
||||
break;
|
||||
}
|
||||
case 'A': case 'a': {
|
||||
// Arc command: consume parameters but approximate as a line
|
||||
// (arcs are rare in these icons)
|
||||
fixed_t rx = pp.read_number();
|
||||
fixed_t ry = pp.read_number();
|
||||
pp.read_number(); // x-rotation
|
||||
pp.read_number(); // large-arc-flag
|
||||
pp.read_number(); // sweep-flag
|
||||
// Elliptical-arc command. We treat the arc as circular using
|
||||
// r = min(|rx|, |ry|), which is exact for symbolic icons (all use
|
||||
// equal axes) and a reasonable approximation otherwise.
|
||||
fixed_t rx_in = pp.read_number();
|
||||
fixed_t ry_in = pp.read_number();
|
||||
pp.read_number(); // x-axis rotation (ignored)
|
||||
fixed_t fa_f = pp.read_number(); // large-arc-flag
|
||||
fixed_t fs_f = pp.read_number(); // sweep-flag
|
||||
fixed_t x = pp.read_number();
|
||||
fixed_t y = pp.read_number();
|
||||
if (cmd == 'a') { x += cur_x; y += cur_y; }
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x, y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
|
||||
fixed_t r = (rx_in < 0) ? -rx_in : rx_in;
|
||||
fixed_t ry_abs = (ry_in < 0) ? -ry_in : ry_in;
|
||||
if (ry_abs < r) r = ry_abs;
|
||||
|
||||
int fa = (fa_f != 0) ? 1 : 0;
|
||||
int fs = (fs_f != 0) ? 1 : 0;
|
||||
|
||||
fixed_t dx = x - cur_x;
|
||||
fixed_t dy = y - cur_y;
|
||||
if ((dx == 0 && dy == 0) || r == 0) {
|
||||
// Per spec: degenerate arc becomes a line.
|
||||
fixed_t sx0, sy0, sx1, sy1;
|
||||
scale_pt(cur_x, cur_y, &sx0, &sy0);
|
||||
scale_pt(x, y, &sx1, &sy1);
|
||||
el.add(sx0, sy0, sx1, sy1);
|
||||
cur_x = x; cur_y = y;
|
||||
last_cmd = cmd;
|
||||
break;
|
||||
}
|
||||
|
||||
// Endpoint-to-center: midpoint of chord, half-chord length,
|
||||
// perpendicular height from chord to center.
|
||||
fixed_t mx = cur_x + dx / 2;
|
||||
fixed_t my = cur_y + dy / 2;
|
||||
fixed_t half_chord = svg_fixed_hypot(dx / 2, dy / 2);
|
||||
if (r < half_chord) r = half_chord; // SVG spec: snap radius up
|
||||
int64_t r_sq = (int64_t)r * r;
|
||||
int64_t hc_sq = (int64_t)half_chord * half_chord;
|
||||
int64_t h_sq = r_sq - hc_sq;
|
||||
if (h_sq < 0) h_sq = 0;
|
||||
fixed_t h = (fixed_t)svg_int64_sqrt((uint64_t)h_sq);
|
||||
|
||||
// Center = mid + sign * (h / chord_len) * (-dy, dx)
|
||||
// sign: spec F.6.5 says -1 when fa == fs, else +1.
|
||||
fixed_t chord_len = 2 * half_chord;
|
||||
if (chord_len == 0) chord_len = 1;
|
||||
fixed_t h_over_chord = fixed_div(h, chord_len);
|
||||
int sign = (fa == fs) ? -1 : 1;
|
||||
fixed_t off_x_perp = fixed_mul(-dy, h_over_chord);
|
||||
fixed_t off_y_perp = fixed_mul( dx, h_over_chord);
|
||||
if (sign < 0) { off_x_perp = -off_x_perp; off_y_perp = -off_y_perp; }
|
||||
fixed_t cx_ctr = mx + off_x_perp;
|
||||
fixed_t cy_ctr = my + off_y_perp;
|
||||
|
||||
if (fa == 1) {
|
||||
// Large arc: bisect at the far point (opposite side of center
|
||||
// from chord midpoint), then walk each half as a small arc.
|
||||
fixed_t vx = mx - cx_ctr;
|
||||
fixed_t vy = my - cy_ctr;
|
||||
fixed_t vlen = svg_fixed_hypot(vx, vy);
|
||||
fixed_t am_x, am_y;
|
||||
if (vlen == 0) {
|
||||
// 180-degree arc: bisect using chord-perpendicular instead.
|
||||
fixed_t k = fixed_div(r, chord_len);
|
||||
fixed_t px = fixed_mul(-dy, k);
|
||||
fixed_t py = fixed_mul( dx, k);
|
||||
if (fs == 0) { px = -px; py = -py; }
|
||||
am_x = cx_ctr + px;
|
||||
am_y = cy_ctr + py;
|
||||
} else {
|
||||
fixed_t k = fixed_div(-r, vlen);
|
||||
am_x = cx_ctr + fixed_mul(vx, k);
|
||||
am_y = cy_ctr + fixed_mul(vy, k);
|
||||
}
|
||||
emit_small_arc(cx_ctr, cy_ctr, r, cur_x, cur_y, am_x, am_y);
|
||||
emit_small_arc(cx_ctr, cy_ctr, r, am_x, am_y, x, y);
|
||||
} else {
|
||||
emit_small_arc(cx_ctr, cy_ctr, r, cur_x, cur_y, x, y);
|
||||
}
|
||||
|
||||
cur_x = x; cur_y = y;
|
||||
last_cmd = cmd;
|
||||
(void)rx; (void)ry;
|
||||
break;
|
||||
}
|
||||
case 'Z': case 'z': {
|
||||
@@ -1027,6 +1163,42 @@ inline bool svg_element_has_filter(const char* elem, int elemLen) {
|
||||
return svg_get_attr(elem, elemLen, " filter", buf, sizeof(buf)) > 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Parse transform="scale(x)" or transform="scale(x,y)" or translate(x,y)
|
||||
// Returns true if a scale was found; sx/sy are fixed-point scale factors.
|
||||
// ---------------------------------------------------------------------------
|
||||
inline bool svg_get_element_scale(const char* elem, int elemLen,
|
||||
fixed_t* sx, fixed_t* sy) {
|
||||
char buf[64];
|
||||
if (svg_get_attr(elem, elemLen, " transform", buf, sizeof(buf)) <= 0) {
|
||||
*sx = int_to_fixed(1);
|
||||
*sy = int_to_fixed(1);
|
||||
return false;
|
||||
}
|
||||
// Look for "scale("
|
||||
const char* sp = buf;
|
||||
while (*sp) {
|
||||
if (sp[0]=='s' && sp[1]=='c' && sp[2]=='a' && sp[3]=='l' && sp[4]=='e' && sp[5]=='(') {
|
||||
sp += 6;
|
||||
svg_parse_fixed(sp, sx);
|
||||
// Skip to comma or closing paren
|
||||
while (*sp && *sp != ',' && *sp != ')') ++sp;
|
||||
if (*sp == ',') {
|
||||
++sp;
|
||||
while (*sp == ' ') ++sp;
|
||||
svg_parse_fixed(sp, sy);
|
||||
} else {
|
||||
*sy = *sx; // uniform scale
|
||||
}
|
||||
return true;
|
||||
}
|
||||
++sp;
|
||||
}
|
||||
*sx = int_to_fixed(1);
|
||||
*sy = int_to_fixed(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scanline rasterizer with alpha blending (for multi-color SVGs)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1314,11 +1486,17 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
|
||||
int alpha = svg_get_element_opacity(elem_start, elem_len);
|
||||
|
||||
// Per-element transform (scale)
|
||||
fixed_t elem_sx, elem_sy;
|
||||
svg_get_element_scale(elem_start, elem_len, &elem_sx, &elem_sy);
|
||||
fixed_t eff_sx = fixed_mul(scale_x, elem_sx);
|
||||
fixed_t eff_sy = fixed_mul(scale_y, elem_sy);
|
||||
|
||||
// Extract and rasterize path
|
||||
int d_len = svg_get_attr(elem_start, elem_len, " d", d_buf, SVG_MAX_PATH_LEN);
|
||||
if (d_len > 0) {
|
||||
el.clear();
|
||||
svg_path_to_edges(el, d_buf, d_len, scale_x, scale_y, vb_x, vb_y);
|
||||
svg_path_to_edges(el, d_buf, d_len, eff_sx, eff_sy, vb_x, vb_y);
|
||||
if (el.count > 0)
|
||||
svg_rasterize_blend(el, icon.pixels, target_w, target_h, elem_color.to_pixel(), alpha);
|
||||
}
|
||||
@@ -1349,6 +1527,11 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
|
||||
int alpha = svg_get_element_opacity(elem_start, elem_len);
|
||||
|
||||
fixed_t elem_sx, elem_sy;
|
||||
svg_get_element_scale(elem_start, elem_len, &elem_sx, &elem_sy);
|
||||
fixed_t eff_sx = fixed_mul(scale_x, elem_sx);
|
||||
fixed_t eff_sy = fixed_mul(scale_y, elem_sy);
|
||||
|
||||
char attr_buf[32];
|
||||
fixed_t cx = 0, cy = 0, r = 0;
|
||||
if (svg_get_attr(elem_start, elem_len, " cx", attr_buf, sizeof(attr_buf)) > 0)
|
||||
@@ -1358,10 +1541,10 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
if (svg_get_attr(elem_start, elem_len, " r", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &r);
|
||||
|
||||
fixed_t scx = fixed_mul(cx - vb_x, scale_x);
|
||||
fixed_t scy = fixed_mul(cy - vb_y, scale_y);
|
||||
fixed_t srx = fixed_mul(r, scale_x);
|
||||
fixed_t sry = fixed_mul(r, scale_y);
|
||||
fixed_t scx = fixed_mul(cx - vb_x, eff_sx);
|
||||
fixed_t scy = fixed_mul(cy - vb_y, eff_sy);
|
||||
fixed_t srx = fixed_mul(r, eff_sx);
|
||||
fixed_t sry = fixed_mul(r, eff_sy);
|
||||
fixed_t sr = (srx + sry) >> 1;
|
||||
|
||||
el.clear();
|
||||
@@ -1395,6 +1578,11 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
|
||||
int alpha = svg_get_element_opacity(elem_start, elem_len);
|
||||
|
||||
fixed_t elem_sx, elem_sy;
|
||||
svg_get_element_scale(elem_start, elem_len, &elem_sx, &elem_sy);
|
||||
fixed_t eff_sx = fixed_mul(scale_x, elem_sx);
|
||||
fixed_t eff_sy = fixed_mul(scale_y, elem_sy);
|
||||
|
||||
char attr_buf[32];
|
||||
fixed_t rx_val = 0, ry_val = 0, rw = 0, rh = 0, rrx = 0, rry = 0;
|
||||
|
||||
@@ -1411,12 +1599,12 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
if (svg_get_attr(elem_start, elem_len, " ry", attr_buf, sizeof(attr_buf)) > 0)
|
||||
svg_parse_fixed(attr_buf, &rry);
|
||||
|
||||
fixed_t sx = fixed_mul(rx_val - vb_x, scale_x);
|
||||
fixed_t sy = fixed_mul(ry_val - vb_y, scale_y);
|
||||
fixed_t sw = fixed_mul(rw, scale_x);
|
||||
fixed_t sh = fixed_mul(rh, scale_y);
|
||||
fixed_t srx = fixed_mul(rrx, scale_x);
|
||||
fixed_t sry = fixed_mul(rry, scale_y);
|
||||
fixed_t sx = fixed_mul(rx_val - vb_x, eff_sx);
|
||||
fixed_t sy = fixed_mul(ry_val - vb_y, eff_sy);
|
||||
fixed_t sw = fixed_mul(rw, eff_sx);
|
||||
fixed_t sh = fixed_mul(rh, eff_sy);
|
||||
fixed_t srx = fixed_mul(rrx, eff_sx);
|
||||
fixed_t sry = fixed_mul(rry, eff_sy);
|
||||
|
||||
el.clear();
|
||||
svg_rect_edges(el, sx, sy, sw, sh, srx, sry);
|
||||
@@ -1438,6 +1626,46 @@ inline SvgIcon svg_render(const char* svg_data, int svg_len, int target_w, int t
|
||||
// ---------------------------------------------------------------------------
|
||||
// Load SVG from VFS and render
|
||||
// ---------------------------------------------------------------------------
|
||||
// Extract intrinsic dimensions from an SVG document. Prefers width/height
|
||||
// attributes, falls back to viewBox, then to a 256x256 default. Used by the
|
||||
// image viewer to render SVGs at their natural size rather than a fixed icon
|
||||
// size.
|
||||
inline void svg_get_natural_size(const char* svg_data, int svg_len,
|
||||
int* out_w, int* out_h) {
|
||||
int w = 0, h = 0;
|
||||
const char* svg_tag = svg_strstr(svg_data, svg_len, "<svg");
|
||||
if (svg_tag) {
|
||||
int tag_offset = (int)(svg_tag - svg_data);
|
||||
int tag_end = tag_offset;
|
||||
while (tag_end < svg_len && svg_data[tag_end] != '>') ++tag_end;
|
||||
int tag_len = tag_end - tag_offset + 1;
|
||||
|
||||
char attr_buf[64];
|
||||
if (svg_get_attr(svg_tag, tag_len, " width", attr_buf, sizeof(attr_buf)) > 0)
|
||||
w = svg_parse_int(attr_buf);
|
||||
if (svg_get_attr(svg_tag, tag_len, " height", attr_buf, sizeof(attr_buf)) > 0)
|
||||
h = svg_parse_int(attr_buf);
|
||||
|
||||
if ((w <= 0 || h <= 0) &&
|
||||
svg_get_attr(svg_tag, tag_len, " viewBox", attr_buf, sizeof(attr_buf)) > 0) {
|
||||
fixed_t vbx, vby, vbw, vbh;
|
||||
const char* vp = attr_buf;
|
||||
int c;
|
||||
c = svg_parse_fixed(vp, &vbx); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
c = svg_parse_fixed(vp, &vby); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
c = svg_parse_fixed(vp, &vbw); vp += c; while (*vp && svg_char_is_sep(*vp)) ++vp;
|
||||
svg_parse_fixed(vp, &vbh);
|
||||
(void)vbx; (void)vby;
|
||||
if (w <= 0) w = fixed_to_int(vbw);
|
||||
if (h <= 0) h = fixed_to_int(vbh);
|
||||
}
|
||||
}
|
||||
if (w <= 0) w = 256;
|
||||
if (h <= 0) h = 256;
|
||||
if (out_w) *out_w = w;
|
||||
if (out_h) *out_h = h;
|
||||
}
|
||||
|
||||
inline SvgIcon svg_load(const char* vfs_path, int target_w, int target_h, Color fill_color) {
|
||||
int fd = montauk::open(vfs_path);
|
||||
if (fd < 0) {
|
||||
|
||||
Reference in New Issue
Block a user