Projects / 01
ovg
Odin vector graphics. A 2D vector canvas that started as a port of femtovg (the nanovg lineage) and grew well beyond it.
- Odin
- MIT
- software rasterizer
- SDL_GPU · Vulkan / Metal / D3D12
- no deps beyond Odin vendor
By the numbers
- of the resvg test suite
- 88.5%
- 1037 files; the curated 53 all pass
- GPU vs software parity
- 1–2/255
- every parity scene, RX 9070 XT, Vulkan (RADV)
- to lay out a 42-character line
- 1.1 µs
- cached; 4.8 µs shaped from scratch
- CSS blend modes
- 16
- plus patterns, masks and clip paths
All figures from the ovg README (tools/svg_suite, tools/sdlgpu_parity, text layout).
What's in it
01The femtovg core
Paths, strokes, gradients, image paints, scissoring, compositing, render-to-image and antialiased fills, recorded as commands for a backend.
02Layers
Group opacity, clip paths, alpha and luminance masks, 16 blend modes and patterns, each sized to its content.
03SVG filters
Blur, drop shadow, offset, flood, composite, merge, blend and colour matrix, with filter regions and linearRGB.
04SVG import
svg_parse / svg_draw with CSS styling, <use>, gradients, patterns, masks, filters and text, plus per-element overrides for animation.
05Lottie import
After Effects (Bodymovin) animations: precomps, mattes, masks, trim paths and eased keyframes.
06Real text
Shaping with kb_text_shape (ligatures, kerning, Arabic and Indic scripts, bidi), per-glyph font fallback and multi-style layout, drawn as vector outlines.
07Dashes
Dash arrays, offsets and pathLength, enough to draw shapes on stroke by stroke.
08Custom shaders
Register compiled SkSL (from oksl) and run it as a layer, with image, video or group children.
09Record anywhere, play on the GPU
A recording canvas builds a frame's commands on any thread; the GPU thread plays them back, so frames build in parallel.
Two backends, one picture
The software backend is the pixel reference. It evaluates the fragment shader's formulas, replays the stencil passes and samples textures exactly like the GPU, so the SDL_GPU backend (Vulkan on Linux, Metal on macOS) can be diffed against it scene by scene. The only dependencies are Odin's own vendor packages: stb for fonts and images, kb_text_shape for shaping, and SDL3 for the GPU path.
Use it
import vg "ovg" // e.g. -collection:libs=.. then "libs:ovg"
sw: vg.Software_Backend
vg.software_backend_init(&sw)
c: vg.Canvas
vg.canvas_init(&c, vg.software_backend(&sw))
vg.set_size(&c, 640, 360)
vg.clear_rect(&c, 0, 0, 640, 360, vg.WHITE)
p: vg.Path
vg.path_rounded_rect(&p, 40, 40, 200, 120, 16)
vg.fill_path(&c, &p, vg.paint_linear_gradient(40, 40, 240, 160, vg.rgb(255, 80, 0), vg.rgb(0, 60, 255)))
stroke := vg.paint_color(vg.BLACK)
stroke.stroke.line_width = 3
vg.stroke_path(&c, &p, stroke)
vg.flush(&c)
vg.write_png("out.png", sw.pixels, sw.width, sw.height)import vgsdl "ovg/sdlgpu"
gpu: vgsdl.SDL_Backend
vgsdl.init(&gpu) // own headless device (or pass the host's)
c: vg.Canvas
vg.canvas_init(&c, vgsdl.backend(&gpu))
// ... draw ...
vg.flush(&c) // renders into the backend's offscreen texture
shot, _ := vg.screenshot(&c) // RGBA8, top-down