v-gui includes a complete SVG rendering pipeline for vector graphics. SVGs are parsed, tessellated into triangles, cached, and rendered efficiently via the GPU.
// From file
gui.svg(file_name: 'icon.svg', width: 24, height: 24)
// Inline SVG data
gui.svg(svg_data: '<svg viewBox="0 0 24 24">...</svg>', width: 24, height: 24)
// With color override (for monochrome icons)
gui.svg(file_name: 'icon.svg', width: 24, height: 24, color: gui.theme().text_color)| Element | Support |
|---|---|
<path> |
Full (all commands: M, L, H, V, C, S, Q, T, A, Z) |
<rect> |
Full (including rx/ry for rounded corners) |
<circle> |
Full |
<ellipse> |
Full |
<line> |
Full |
<polygon> |
Full |
<polyline> |
Full |
<g> |
Full (nested groups with style inheritance) |
<clipPath> |
Full (binary stencil clipping via <defs>) |
All SVG transform functions are supported:
<g transform="translate(10, 20)">...</g>
<g transform="rotate(45)">...</g>
<g transform="rotate(45, 100, 100)">...</g> <!-- rotate around point -->
<g transform="scale(2)">...</g>
<g transform="scale(2, 0.5)">...</g>
<g transform="skewX(30)">...</g>
<g transform="skewY(30)">...</g>
<g transform="matrix(a, b, c, d, e, f)">...</g>
<!-- Multiple transforms are composed -->
<g transform="translate(50, 50) rotate(45) scale(2)">...</g><!-- Fill colors -->
<path fill="#ff0000" ... />
<path fill="rgb(255, 0, 0)" ... />
<path fill="red" ... />
<path fill="none" ... />
<!-- Strokes -->
<path stroke="#000" stroke-width="2" ... />
<path stroke-linecap="round" ... /> <!-- butt, round, square -->
<path stroke-linejoin="bevel" ... /> <!-- miter, round, bevel -->Styles cascade from parent groups to children:
<g fill="blue" stroke="black" stroke-width="2">
<rect ... /> <!-- inherits blue fill, black stroke -->
<circle fill="red" /> <!-- red fill, inherits black stroke -->
</g>Clip paths use stencil-based clipping to restrict rendering to a defined shape:
<defs>
<clipPath id="myClip">
<circle cx="50" cy="50" r="40"/>
</clipPath>
</defs>
<rect width="100" height="100" fill="blue" clip-path="url(#myClip)"/>The clip-path attribute works on individual shapes and <g>
groups. Group clip paths are inherited by children.
SVGs are parsed into VectorPath structures containing:
- Path segments (move, line, quadratic/cubic bezier, arc, close)
- Fill color
- Stroke properties (color, width, cap, join)
- Transform matrix
Paths are converted to triangles for GPU rendering:
- Curve flattening: Bezier curves subdivided to polylines based on tolerance
- Transform application: Affine transforms applied to all coordinates
- Fill tessellation: Ear clipping algorithm with hole support
- Stroke tessellation: Polylines expanded to quads with proper joins and caps
Tessellated SVGs are cached by source and size:
// Automatic caching - same SVG at same size reuses tessellation
gui.svg(file_name: 'icon.svg', width: 24, height: 24) // tessellates
gui.svg(file_name: 'icon.svg', width: 24, height: 24) // cache hit
// Different size = new cache entry
gui.svg(file_name: 'icon.svg', width: 48, height: 48) // tessellates at new scaleManual cache control:
// Clear specific SVG from cache
window.remove_svg_from_cache('icon.svg')
// Clear all cached SVGs
window.clear_svg_cache()gui.row(
content: [
gui.svg(file_name: 'save.svg', width: 16, height: 16),
gui.text(text: 'Save'),
]
)gui.svg(
file_name: 'settings.svg',
width: 24,
height: 24,
color: gui.theme().text_color, // overrides SVG fill colors
)// See examples/tiger.v for complete example
gui.svg(file_name: 'tiger.svg', width: 450, height: 450)The classic Ghostscript Tiger (240 paths with transforms, groups, and strokes) renders correctly, demonstrating full SVG compatibility.
To prevent denial-of-service attacks, the following limits are enforced:
- Elements: 100,000 max (most icons have <10; complex SVGs like tiger.svg: ~240)
- Path segments: 100,000 max per path (typical paths: <50)
- Group nesting: 32 levels max (prevents stack overflow)
- ViewBox dimensions: 10,000 max (prevents extreme allocations)
- Coordinates: ±1,000,000 max (prevents integer overflow)
- Attribute length: 1MB max (prevents excessive string allocations)
SVGs exceeding these limits will be partially rendered or rejected. For production use, optimize SVG files to stay well within limits.
SVGs are cached for performance:
- Capacity: 100 entries (unique source + size combinations)
- Eviction: LRU (least recently accessed)
- Key:
hash(source):width×10:height×10(0.1px precision) - Memory: Each entry stores tessellated triangles (~1-10MB for complex SVGs)
- Limit: SVGs generating >10MB triangles bypass cache (still render)
Cache invalidation: Call window.clear_svg_cache() or
window.remove_svg_from_cache(source) after modifying files.
Cache efficiency: SVGs at different sizes are cached separately. Round sizes to 0.1px to maximize reuse (e.g., 24.0, 32.0, 48.0).
Optimize SVG files:
- Remove editor metadata (
<metadata>,<sodipodi>tags) - Merge paths where possible (reduces element count)
- Simplify transforms (inline when possible)
- Use integers for coordinates (faster parsing)
When to pre-render: For SVGs with >100 paths or heavy use of filters/masks (not supported), consider rasterizing to PNG at target sizes.
Rendering performance:
- First load: ~1-5ms parse + tessellate (depends on complexity)
- Cached: ~0.01ms (hash lookup + GPU upload)
- Frame time: <0.1ms per SVG (GPU batched)
- Gradients, patterns, filters
<use>(symbol reuse)<mask>(luminance alpha masking)<linearGradient>and<radialGradient>- CSS styling (
<style>blocks,classattributes) opacityattribute- Text (
<text>,<tspan>) - Filters (
<filter>)
For icons and illustrations, these limitations rarely matter. For complex SVGs with gradients or text, consider converting to supported features or using bitmap images.
-
Use appropriate sizes: Tessellation quality scales with display size. Don't render a 1000x1000 SVG at 24x24.
-
Reuse SVGs: The cache is keyed by source+size. Identical SVG widgets share tessellation.
-
Simplify paths: Fewer path segments = faster tessellation. Tools like SVGO can optimize.
-
Color override for icons: Using
color:parameter is faster than parsing colors from SVG.