libsvga is the Zig core for a new SVGA implementation. It owns platform-neutral
SVGA parsing and timeline data, while platform packages such as Swift own UI,
image decoding, animation clocks, audio, and rendering.
The current milestone targets parser parity with SVGAPlayer-iOS for non-UI
code. libsvga parses plain zlib/protobuf SVGA 2.x files, zip archives with
movie.binary, and legacy zip archives with movie.spec JSON into normalized
movie and timeline data:
- SVGA version
- view box width and height
- FPS
- frame count
- image, sprite, asset, and audio counts
- filename assets, embedded image bytes, embedded audio bytes, and zip payloads
- sprite image/matte keys
- audio timing records
- frame alpha, layout, transform, clip path, and shape counts
- full vector shape records: path, rect, ellipse, keep, styles, and transform
- precomputed frame visibility and transformed
nx/nyminima - first-shape
keepmarkers for vector frame caching - asset-key resolution helpers, including filename indirection and
.pngfallback used by SVGA packages - scalar playback and layout helpers for frame/time mapping, loop ranges, and viewport placement
Bitmap decoding, audio playback, layer construction, and animation clocks remain platform/UI responsibilities.
Zig 0.15.2 is currently used.
ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test
ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig buildThe build produces:
zig-out/lib/libsvga.azig-out/include/svga.hzig-out/bin/svga_probe
Native Darwin builds default to the platform libz inflater for SVGA zlib and
ZIP/deflate payloads because it is materially faster on the production corpus.
Pass -Dsystem-zlib=false to force Zig's portable std.compress.flate
inflater. Non-Darwin builds default to the portable inflater.
Release package builds pass -Dsystem-zlib=false explicitly so published
portable archives and the SwiftPM XCFramework do not require consumers to link a
target libz.
On Darwin targets, zig build re-archives the installed static library with
Apple libtool/ranlib so SwiftPM/Xcode's linker accepts
zig-out/lib/libsvga.a.
The Apple release package step repeats that re-archive immediately before
creating the XCFramework and verifies 64-bit Mach-O archive member alignment.
Build release archives with:
zig build package-release -Doptimize=ReleaseFast -Drelease-version=0.2.5The package step produces:
- Android static library tarballs
- WASM static library tarballs
libsvga-apple-xcframework-<version>.tar.gzlibsvga-static-<version>.xcframework.zip
The .xcframework.zip puts libsvga-static.xcframework at the zip root so
SwiftPM can reference it with a binary target URL. The current XCFramework
contains macOS arm64, iOS device arm64, and iOS simulator arm64 slices.
Zig consumers should import the package module and use the native core API:
const libsvga = @import("libsvga");
const movie = try libsvga.parseMovieFile(allocator, path, .{});
defer libsvga.destroyMovie(allocator, movie);
const remote_movie = try libsvga.downloadMovie(allocator, "https://example.com/anim.svga", .{});
defer libsvga.destroyMovie(allocator, remote_movie);The public C ABI is declared in include/svga.h.
The core API uses opaque handles and explicit ownership:
svga_status_t svga_movie_parse(
const uint8_t *bytes,
size_t byte_count,
svga_movie_t **out_movie
);
svga_status_t svga_movie_parse_file(
const char *path_utf8,
svga_movie_t **out_movie
);
svga_status_t svga_movie_download(
const char *url_utf8,
const svga_download_options_t *options,
svga_movie_t **out_movie
);
svga_status_t svga_movie_get_info(
const svga_movie_t *movie,
svga_movie_info_t *out_info
);
svga_status_t svga_movie_get_sprite_info(
const svga_movie_t *movie,
uint32_t sprite_index,
svga_sprite_info_t *out_info
);
svga_status_t svga_movie_get_frame_info(
const svga_movie_t *movie,
uint32_t sprite_index,
uint32_t frame_index,
svga_frame_info_t *out_info
);
svga_status_t svga_movie_get_asset_count(
const svga_movie_t *movie,
uint32_t *out_count
);
svga_status_t svga_movie_get_asset_info(
const svga_movie_t *movie,
uint32_t asset_index,
svga_asset_info_t *out_info
);
svga_status_t svga_movie_find_asset(
const svga_movie_t *movie,
const char *key_utf8,
svga_asset_info_t *out_info
);
svga_status_t svga_movie_resolve_image_asset(
const svga_movie_t *movie,
const char *image_key_utf8,
svga_asset_info_t *out_info
);
svga_status_t svga_movie_resolve_audio_asset(
const svga_movie_t *movie,
const char *audio_key_utf8,
svga_asset_info_t *out_info
);
svga_status_t svga_movie_get_audio_info(
const svga_movie_t *movie,
uint32_t audio_index,
svga_audio_info_t *out_info
);
svga_status_t svga_movie_get_shape_info(
const svga_movie_t *movie,
uint32_t sprite_index,
uint32_t frame_index,
uint32_t shape_index,
svga_shape_info_t *out_info
);
void svga_movie_destroy(svga_movie_t *movie);Strings and byte buffers returned through info structs are borrowed from the
movie handle and remain valid until svga_movie_destroy.
The default parse APIs build full metadata, SVG path-command, render, and
visual-frame tables. Callers that only need metadata can use
svga_movie_parse_with_options_and_model_mode or
svga_movie_parse_file_with_options_and_model_mode with
SVGA_PARSE_MODEL_MODE_NO_RENDER or SVGA_PARSE_MODEL_MODE_METADATA_ONLY to
skip selected derived tables. Existing svga_parse_options_t layout remains
unchanged for ABI compatibility. Render capability queries are derived from the
built render tables, so use the default full mode when capability inspection is
required.
svga_movie_parse_file reads local filesystem paths in Zig before parsing, so
platform bindings do not need to materialize an intermediate byte buffer when
they already have a file URL.
svga_movie_download downloads an HTTP/HTTPS response into a bounded memory
buffer and then calls the same byte parser. Compressed zlib and ZIP payloads are
also inflated under a bounded output limit. The downloader does not write the
response to disk; platform layers should still own cache policy, custom headers,
and session/authentication behavior.
Missing asset keys return SVGA_STATUS_INVALID_ARGUMENT and clear the output
record. Resolved image/audio asset helpers preserve SVGAPlayerSwift's filename
policy: exact key lookup first; filename assets resolve to name.png and then
name, unless the filename already ends in .png.
The C ABI also exposes platform-neutral scalar helpers for:
- frame/time conversion
- playback range clamping, reverse offsets, loop completion, and fill-frame selection
- movie-to-viewport layout and aspect-fit rectangles
svga_probe parses one or more .svga files and prints movie metadata.
zig-out/bin/svga_probe path/to/file.svgaThe parser-parity fixture runs currently pass:
- archived SVGAPlayer-iOS samples:
ok=10 failed=0 - private production
.svgaresources:ok=175 failed=0
The standalone benchmark compares libsvga against the archived
SVGAPlayer-iOS parser on private fixtures. Native Apple developer builds use
system zlib by default; portable release packages keep -Dsystem-zlib=false so
they do not require a target zlib at package build time. On the 3,374-file COS
production zlib corpus with ITERATIONS=3:
zig/libsvga:1661328.3 ns_per_parseobjc/SVGAPlayer-iOS:4571168.0 ns_per_parse
Use zig build -Doptimize=ReleaseFast phase-bench -- <fixture-dir> for
internal phase timings.
libsvga should own:
- container detection
- optional direct HTTP/HTTPS download-to-memory parsing
- zlib/zip decode
- protobuf and legacy JSON parsing
- normalized movie, sprite, frame, shape, audio, and asset records
- scalar frame/timeline lookup helpers
- asset indirection policy and viewport layout math
Platform layers should own:
- network session policy, request headers, authentication, and filesystem cache policy
UIImage/CGImage/bitmap decodeCALayer,CAShapeLayer, SwiftUI, or Android rendering- audio playback
- dynamic text/image/drawing replacement