i_slint_core/graphics/image/
svg.rs1use super::{ImageCacheKey, SharedImageBuffer, SharedPixelBuffer};
5use crate::lengths::PhysicalPx;
6#[cfg(not(target_arch = "wasm32"))]
7use crate::SharedString;
8use resvg::{tiny_skia, usvg};
9
10pub struct ParsedSVG {
11 svg_tree: usvg::Tree,
12 cache_key: ImageCacheKey,
13}
14
15impl super::OpaqueImage for ParsedSVG {
16 fn size(&self) -> crate::graphics::IntSize {
17 self.size()
18 }
19 fn cache_key(&self) -> ImageCacheKey {
20 self.cache_key.clone()
21 }
22}
23
24impl core::fmt::Debug for ParsedSVG {
25 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26 f.debug_tuple("ParsedSVG").finish()
27 }
28}
29
30impl ParsedSVG {
31 pub fn size(&self) -> crate::graphics::IntSize {
32 let size = self.svg_tree.size().to_int_size();
33 [size.width(), size.height()].into()
34 }
35
36 pub fn cache_key(&self) -> ImageCacheKey {
37 self.cache_key.clone()
38 }
39
40 #[allow(clippy::unnecessary_cast)] pub fn render(
43 &self,
44 size: Option<euclid::Size2D<u32, PhysicalPx>>,
45 ) -> Result<SharedImageBuffer, usvg::Error> {
46 let tree = &self.svg_tree;
47
48 let (target_size, transform) = match size {
49 Some(size) => {
50 let target_size = tiny_skia::IntSize::from_wh(size.width, size.height)
51 .ok_or(usvg::Error::InvalidSize)?;
52 let target_size = tree.size().to_int_size().scale_to(target_size);
53 let target_size_f = target_size.to_size();
54
55 let transform = tiny_skia::Transform::from_scale(
56 target_size_f.width() as f32 / tree.size().width() as f32,
57 target_size_f.height() as f32 / tree.size().height() as f32,
58 );
59 (target_size, transform)
60 }
61 None => (tree.size().to_int_size(), tiny_skia::Transform::default()),
62 };
63
64 let mut buffer = SharedPixelBuffer::new(target_size.width(), target_size.height());
65 let mut skia_buffer = tiny_skia::PixmapMut::from_bytes(
66 buffer.make_mut_bytes(),
67 target_size.width(),
68 target_size.height(),
69 )
70 .ok_or(usvg::Error::InvalidSize)?;
71
72 resvg::render(tree, transform, &mut skia_buffer);
73 Ok(SharedImageBuffer::RGBA8Premultiplied(buffer))
74 }
75}
76
77#[cfg(not(target_arch = "wasm32"))]
78pub fn load_from_path(
79 path: &SharedString,
80 cache_key: ImageCacheKey,
81) -> Result<ParsedSVG, std::io::Error> {
82 let svg_data = std::fs::read(std::path::Path::new(&path.as_str()))?;
83
84 let option = usvg::Options::default();
85 usvg::Tree::from_data(&svg_data, &option)
86 .map(|svg| ParsedSVG { svg_tree: svg, cache_key })
87 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
88}
89
90pub fn load_from_data(slice: &[u8], cache_key: ImageCacheKey) -> Result<ParsedSVG, usvg::Error> {
91 let option = usvg::Options::default();
92 usvg::Tree::from_data(slice, &option).map(|svg| ParsedSVG { svg_tree: svg, cache_key })
93}