Skip to main content

lumen_assets/loaders/
svg.rs

1//! SVG loader: parses with usvg and keeps the tree for the renderer to paint.
2
3use crate::{AssetKind, AssetLoader, LoadContext, LoadErrorKind, LoadedAsset, LoadedSvg, SvgData};
4
5/// Extensions the SVG loader claims.
6pub const SVG_EXTENSIONS: &[&str] = &["svg"];
7
8/// Parses an SVG into a [`SvgData`].
9///
10/// Parsing happens once, at load time, on the asset worker; the renderer
11/// paints the parsed tree and caches the drawing where its backend can. The
12/// source file length is recorded as the payload's byte cost, because usvg
13/// does not expose the size of a parsed tree.
14pub struct SvgLoader;
15
16impl AssetLoader for SvgLoader {
17    fn extensions(&self) -> &[&str] {
18        SVG_EXTENSIONS
19    }
20
21    fn kind(&self) -> AssetKind {
22        AssetKind::Svg
23    }
24
25    fn load(&self, ctx: &LoadContext<'_>) -> Result<LoadedAsset, LoadErrorKind> {
26        let path = ctx.path();
27        let bytes = ctx.read_bytes()?;
28        let source_bytes = bytes.len();
29        let opt = usvg::Options::default();
30        let tree = usvg::Tree::from_data(&bytes, &opt)
31            .map_err(|e| LoadErrorKind::DecodeFailed(format!("{path:?}: {e}")))?;
32        let size = tree.size();
33        let data = SvgData {
34            intrinsic: glam::Vec2::new(size.width(), size.height()),
35            tree,
36            id: SvgData::next_id(),
37            source_bytes,
38        };
39        Ok(LoadedAsset::Svg(LoadedSvg(data.into())))
40    }
41}