|
|
Donner SVG Editor & Engine
SVG-native editor and embeddable SVG2 + CSS3 engine in C++20, with GPU (WebGPU) and compact CPU renderers, built for correctness, security, and performance.
|
Status: Developer reference. Describes Geode's as-built implementation of the Slug algorithm (per-pixel ray/Bézier-crossing winding) — the pipeline a contributor must understand to work on the fill/coverage shaders or the path encoder — plus the invariants it relies on and the known limitations (D3/D4/D6) that remain future work. The per-pixel AA coverage math is documented in 0041; this doc covers the rest of the pipeline.
Related: 0041 anti-aliasing, 0038 text parity, 0021
Geode implements the Slug algorithm clean-room (ISC). The algorithmic references are:
The patent (US10373352B1) was dedicated to the public domain on 2026-03-17; reference shaders are MIT. Geode's implementation is independent.
Geode renders a Path by encoding it into band-binned Y-monotonic quadratic curves on the CPU, then per-pixel casting a horizontal ray and counting winding from the curves in the pixel's band. Two halves:
GeodePathEncoder::encode lowers a Path into an EncodedPath of bands + quadratic curves for the GPU:
Per pixel, fs_main looks up its horizontal and vertical band in O(1) from a dense grid and casts two rays (the analytic dual-ray scheme from 0041, landed):
slug_fill, slug_gradient, and slug_mask all run this same dual-ray accumulation and solve_quadratic core. The vendor-gated *_alpha_coverage shader variants and the 4× MSAA sample_mask path described in earlier revisions of this doc are deleted — see 0041 for the migration.
Severity: Critical = correctness/parity bug, broad blast radius; Moderate = narrower or conditional; Benign = legitimate adaptation or already-correct. Most are resolved (implementation notes / invariants in §1); the open ones are the known limitations in §3.
| # | Sev | Area | Geode location | Status |
|---|---|---|---|---|
| D1 | Critical | solve_quadratic numerical stability | slug_fill.wgsl, slug_gradient.wgsl, slug_mask.wgsl | resolved — Citardauq form (§1.2 step 3) |
| D2 | Critical (latent) | encoder must not silently flatten cubics | GeodePathEncoder.cc:185 | resolved — hard-fail assert documents the cubic-free invariant (§1.1) |
| D3 | Critical | pixels-per-em scale ignores rotation/shear | slug_fill.wgsl:362 | resolved — ppem = 1/fwidth(sample_pos) (§1.2 step 1), landed with the 0041 dual-ray rewrite |
| D4 | Moderate | band binning over-includes curves; no per-band sort | GeodePathEncoder.cc | open (perf only; correctness benign) — see §3 |
| D5 | Moderate | even-odd fill rule | slug_fill.wgsl:387-394 | resolved, and now a real fractional fold — even-odd folds the unsaturated dual-ray coverage through a triangle wave (§1.2 step 6); no longer integer parity |
| D6 | Moderate | absolute degeneracy threshold + missing b≈0 NaN guard | slug_fill.wgsl:204 | open / known limitation (§3) — now user-visible since coverage is fractional |
| D7 | — | (0x2E74 classification) | — | moot — Geode's dual-ray shader (§1.2) uses direct per-root sign-of-tangent classification, not the reference's 0x2E74 lookup table; algorithmically equivalent, different implementation |
| D8 | Benign | orthographic-2D half-pixel dilation | slug_fill.wgsl vertex stage | no action — correct for affine 2D |
| D9 | Benign | lines as degenerate quads (control pt = midpoint) | GeodePathEncoder.cc:147 | no action — standard Slug |
| D10 | Benign | implicit-close of open subpaths | GeodePathEncoder.cc:116 | no action — SVG-fill conformance |
| D11 | Benign | ~32px/band, max 256 | GeodePathEncoder.cc:37 | no action — impl-defined granularity |
| D12 | Benign | f32 SSBO curve storage vs reference f16 textures | slug_fill.wgsl | no action — more precise WebGPU adaptation |
D1 — solve_quadratic stability. The shaders previously used the naive (-b ± √disc)/2a, which loses precision to subtractive cancellation when b and a root share a sign — worst exactly at the tangent/grazing crossings the winding count is most sensitive to. Replaced uniformly with the Citardauq form (§1.2 step 3). A robustness improvement for grazing crossings.
D2 — cubic→chord landmine. extractCurves's CurveTo case used to discard both cubic control points and emit a quad with control point = endpoint midpoint = a straight chord — silently wrong geometry. Dead in current call paths (encode runs cubicToQuadratic first), but a future caller handing a raw cubic in would have silently flattened every cubic. Replaced with UTILS_RELEASE_ASSERT_MSG(false, …) documenting the cubic-free invariant — hard-fail instead of silent corruption. Unreachable today, so output-neutral.
D3/D5 — the analytical-AA rework landed. Earlier revisions of this doc described D3 (per-pixel scale) and D5 (fractional even-odd fold) as open/moot future work gated behind a "shelved" analytical-AA rework. That rework is 0041, which has since landed: slug_fill.wgsl now computes ppem via fwidth (D3's intended fix) and folds even-odd coverage through a genuine triangle wave (D5). See §1.2 for the as-built pipeline.
These are genuine, still-open divergences from the published method, on the current dual-ray analytic pipeline (0041, landed).