svg-renderer 1.0.2

A high-performance SVG-to-raster renderer powered by Skia, with CPU and optional Vulkan GPU backends
Documentation

svg-renderer converts SVG documents to raster images (raw RGBA, PNG, JPEG, WebP) with automatic backend selection. It prefers Vulkan GPU acceleration when available and gracefully falls back to CPU rendering.

This crate was developed with Vibe Coding using GPT-5.5.


Why svg-renderer?

Need Solution
Quick SVG → PNG/JPEG/WebP in a CLI tool Free-standing functions or SvgRenderer
Batch-rendering thousands of SVGs SvgPipelineRenderer with N worker threads
Always CPU, no GPU dependency CpuSvgRenderer / CpuSvgPipelineRenderer
Explicit GPU-only path VulkanSvgRenderer / VulkanSvgPipelineRenderer (with vulkan-backend)
SVG references external fonts / images set_resource_search_dirs(…) on any renderer
Fine-grained output format control JpegOptions, WebpOptions structs

Installation

[dependencies]

svg-renderer = "1.0.2"

Default features enable the Vulkan backend. For CPU-only:

[dependencies]

svg-renderer = { version = "1.0.2", default-features = false }

Feature flags

Feature Default Description
vulkan-backend Vulkan GPU rendering via ash + Skia Vulkan integration
(none) CPU rendering only (always available)

Requirements

  • A Rust toolchain compatible with Edition 2024
  • Skia build / runtime dependencies (via skia-safe)
  • Vulkan drivers + runtime — only if using the Vulkan backend

skia-bindings binary download proxy

skia-safe builds through skia-bindings, which tries to download a prebuilt Skia binary during compilation. If that download fails because GitHub or the binary host is unreachable, configure a curl proxy and run the build again.

For a one-off build:

$env:HTTPS_PROXY = "http://127.0.0.1:7890"
$env:HTTP_PROXY = "http://127.0.0.1:7890"
cargo build

To set the proxy in PowerShell for the current terminal and permanently add user-level proxy environment variables:

$proxy = "http://127.0.0.1:7890"

$env:HTTP_PROXY = $proxy
$env:HTTPS_PROXY = $proxy

[Environment]::SetEnvironmentVariable("HTTP_PROXY", $proxy, "User")
[Environment]::SetEnvironmentVariable("HTTPS_PROXY", $proxy, "User")

Write-Host "Proxy has been set to $proxy."
Write-Host "The current terminal can use it immediately; new terminals will inherit it."

If you prefer CMD, save and run this batch script:

@echo off
set "PROXY=http://127.0.0.1:7890"

rem Apply to the current terminal session immediately.
set HTTP_PROXY=%PROXY%
set HTTPS_PROXY=%PROXY%

rem Persist for new terminal sessions.
setx HTTP_PROXY "http://127.0.0.1:7890"
setx HTTPS_PROXY "http://127.0.0.1:7890"

echo Proxy has been set to %PROXY%.
echo The current terminal can use it immediately; new terminals will inherit it.
pause

For a global curl proxy, create or edit the curl configuration file:

  • Windows: %USERPROFILE%\.curlrc
  • Linux / macOS: ~/.curlrc
proxy = "http://127.0.0.1:7890"

Use your own proxy address and port. Remove the setting after the build if you do not want all curl requests to use that proxy.


Quick start

One-liner free functions

use svg_renderer::{render_svg_to_png, RenderOptions};

let png = render_svg_to_png(
    br#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
         <circle cx="100" cy="100" r="80" fill="#0f766e"/></svg>"#,
    &RenderOptions::new(200, 200)?,
)?;
std::fs::write("circle.png", png)?;

Using SvgRenderer (auto-detect backend)

use svg_renderer::{RenderOptions, SvgRenderer};

let mut renderer = SvgRenderer::new()?;
let image = renderer.render_svg(
    br#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 120">
         <rect width="300" height="120" fill="#0f766e"/>
         <text x="24" y="72" font-size="32" fill="white">svg-renderer</text></svg>"#,
    &RenderOptions::new(300, 120)?,
)?;
println!("{}x{} RGBA image", image.width, image.height);

Check which backend was selected:

let mut renderer = SvgRenderer::new()?;
println!("{:?}", renderer.backend()); // Cpu or Vulkan

Output formats

PNG

use svg_renderer::{RenderOptions, SvgRenderer};
let mut renderer = SvgRenderer::new()?;
let png = renderer.render_svg_to_png(svg_bytes, &options)?;

JPEG

use svg_renderer::{
    JpegAlphaOption, JpegDownsample, JpegOptions, RenderOptions, SvgRenderer,
};

let options = RenderOptions::new(512, 320)?;
let jpeg_opts = JpegOptions {
    quality: 90,
    downsample: JpegDownsample::BothDirections,
    alpha_option: JpegAlphaOption::BlendOnBlack,
};
let mut renderer = SvgRenderer::new()?;
let jpeg = renderer.render_svg_to_jpeg(svg_bytes, &options, jpeg_opts)?;
JpegDownsample Behavior
BothDirections 4:2:0 chroma subsampling (default)
Horizontal 4:2:2
No 4:4:4 (best quality, largest file)
JpegAlphaOption Behavior
Ignore Discard alpha
BlendOnBlack Composite over black before encoding

WebP

use svg_renderer::{RenderOptions, SvgRenderer, WebpCompression, WebpOptions};

let options = RenderOptions::new(512, 320)?;
let webp_opts = WebpOptions {
    compression: WebpCompression::Lossy,
    quality: 90.0,
};
let mut renderer = SvgRenderer::new()?;
let webp = renderer.render_svg_to_webp(svg_bytes, &options, webp_opts)?;
WebpCompression Description
Lossy VP8 (default)
Lossless VP8L

Backend selection

Type Feature Behavior
SvgRenderer (any) Try Vulkan → fallback to CPU
CpuSvgRenderer (always) CPU raster, no fallback needed
VulkanSvgRenderer vulkan-backend GPU only, errors on Vulkan failure

CPU renderer — no GPU dependency, identical API:

use svg_renderer::CpuSvgRenderer;
let mut renderer = CpuSvgRenderer::new()?;
let png = renderer.render_svg_to_png(svg_bytes, &options)?;

Vulkan renderer — explicit GPU path:

use svg_renderer::VulkanSvgRenderer; // requires "vulkan-backend"
let mut renderer = VulkanSvgRenderer::new()?;
let png = renderer.render_svg_to_png(svg_bytes, &options)?;

Pipeline rendering (batch workloads)

SvgPipelineRenderer spawns a pool of dedicated worker threads and dispatches jobs via round-robin. Each worker owns its own renderer — ideal for rendering many SVGs concurrently.

use svg_renderer::{RenderOptions, SvgPipelineRenderer};

let renderer = SvgPipelineRenderer::new(4)?;  // 4 workers
let options = RenderOptions::new(512, 320)?;
let svg = b"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 320'/>";

// All pipeline methods are async
let image = renderer.render_svg(svg, &options).await?;
let png   = renderer.render_svg_to_png(svg, &options).await?;
let jpeg  = renderer.render_svg_to_jpeg(svg, &options, jpeg_opts).await?;
let webp  = renderer.render_svg_to_webp(svg, &options, webp_opts).await?;

Pipeline variants mirror the single-shot renderers:

Pipeline type Backend
SvgPipelineRenderer Auto-select (Vulkan → CPU)
CpuSvgPipelineRenderer CPU only
VulkanSvgPipelineRenderer Vulkan only (requires vulkan-backend)

External resources

SVG files may reference external fonts, images, or other assets. Set one or more search directories on any renderer:

let mut renderer = SvgRenderer::new()?;
renderer.set_resource_search_dirs(["assets"]);

The renderer probes:

  1. Local search directories (in order)
  2. HTTP(S) fallback via ureq (built into Skia's resource provider)

Absolute paths in SVG references resolve directly. URL-like references skip local lookup and fall back to HTTP(S).


Image data

pub struct ImageData {
    pub width: u32,
    pub height: u32,
    pub row_bytes: usize,   // always width * 4
    pub rgba: Vec<u8>,      // premultiplied alpha, RGBA8888, row-major
}

Error handling

All public APIs return Result<_, SvgRenderError>.

pub enum SvgRenderError {
    InvalidSize { width: u32, height: u32 },
    InvalidWorkerCount { workers: usize },
    VulkanLoader(ash::LoadingError),       // cfg(vulkan-backend)
    Vulkan(vk::Result),                    // cfg(vulkan-backend)
    NoVulkanDevice,                        // cfg(vulkan-backend)
    SkiaContext,                           // cfg(vulkan-backend)
    SvgParse,
    RenderTarget,
    ReadPixels,
    PngEncode,
    JpegEncode,
    WebpEncode,
    PipelineClosed,
}

Examples

All examples can be found in the examples/ directory.

render_svg

Basic single-shot rendering to multiple output formats.

cargo run --example render_svg

Output in target/example-output/:

  • sample.rgba — raw RGBA data
  • sample.png
  • sample.jpg
  • sample.webp

render_pipeline

Async pipeline rendering with 4 worker threads using SvgPipelineRenderer.

cargo run --example render_pipeline

Writes target/example-output/pipeline-sample.png.

render_perf

Benchmark all available backends (CPU, Vulkan, pipeline) and reports throughput. Accepts optional arguments:

# 100 iterations, 4 pipeline workers (defaults)

cargo run --example render_perf --release


# Custom parameters

cargo run --example render_perf --release -- 500 8

Reports: average/median/min/max latency, FPS throughput.


License

Licensed under the MIT License.