From 319c6f0721c6b2fb586cdb7e42ed28636633a25a Mon Sep 17 00:00:00 2001 From: Marie Katrine Ekeberg Date: Mon, 2 Sep 2024 13:17:49 +0200 Subject: [PATCH] SVG deserialization support (#37) * Add basic SVG rendering functionality * Refactor to avoid unreachable errors during wasm build * Add note on SVG support in readme * Remove redundant return statements and cfg invocation --- Cargo.toml | 3 +++ README.md | 5 +++-- src/io.rs | 27 ++++++++++++++-------- src/io/img.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +++++ test_data/test.svg | 10 +++++++++ 6 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 test_data/test.svg diff --git a/Cargo.toml b/Cargo.toml index aee5abe..27cb015 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ tga = ["image/tga"] gif = ["image/gif"] bmp = ["image/bmp"] +svg = ["dep:resvg"] + obj = ["wavefront_obj"] gltf = ["dep:gltf"] stl = ["dep:stl_io"] @@ -50,6 +52,7 @@ gltf = { version = "1", optional = true, features=["KHR_materials_ior", "KHR_mat wavefront_obj = { version = "10", optional = true } stl_io = { version = "0.8.2", optional = true } image = { version = "0.24", optional = true, default-features = false} +resvg = { version = "0.43.0", optional = true } pcd-rs = { version = "0.10", optional = true, features = ["derive"] } data-url = {version = "0.3", optional = true } serde = {version= "1", optional = true, features = ["derive", "rc"] } diff --git a/README.md b/README.md index 449f3e4..dd77324 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,15 @@ When in memory, the assets can be for example be ### Texture2D | Format | Deserialize | Serialize | Feature | -| ------ | ----------- | --------- | ------- | +|--------|-------------|-----------|---------| | PNG | ✅ | ✅ | `png` | -| JPEG | ✅ | ✅ |  `jpeg` | +| JPEG | ✅ | ✅ | `jpeg` | | HDR | ✅ | ❌ | `hdr` | | GIF | ✅ | ✅ | `gif` | | TGA | ✅ | ✅ | `tga` | | TIFF | ✅ | ✅ | `tiff` | | BMP | ✅ | ✅ | `bmp` | +| SVG | ✅ | ❌ | `svg` | ### PointCloud diff --git a/src/io.rs b/src/io.rs index 2932ddf..31682cb 100644 --- a/src/io.rs +++ b/src/io.rs @@ -133,19 +133,28 @@ use std::path::{Path, PathBuf}; impl Deserialize for crate::Texture2D { fn deserialize(path: impl AsRef, raw_assets: &mut RawAssets) -> Result { let path = raw_assets.match_path(path.as_ref())?; + let extension = path + .extension() + .map(|e| e.to_str().unwrap()) + .unwrap_or("image") + .to_string(); #[allow(unused_variables)] let bytes = raw_assets.get(&path)?; - #[cfg(not(feature = "image"))] - return Err(Error::FeatureMissing( - path.extension() - .map(|e| e.to_str().unwrap()) - .unwrap_or("image") - .to_string(), - )); + if "svg" == extension { + // to satisfy the compiler during wasm compile + #[cfg(not(feature = "svg"))] + return Err(Error::FeatureMissing("svg".to_string())); - #[cfg(feature = "image")] - img::deserialize_img(path, bytes) + #[cfg(feature = "svg")] + img::deserialize_svg(path, bytes) + } else { + #[cfg(not(feature = "image"))] + return Err(Error::FeatureMissing(extension)); + + #[cfg(feature = "image")] + img::deserialize_img(path, bytes) + } } } diff --git a/src/io/img.rs b/src/io/img.rs index df4f479..a925c42 100644 --- a/src/io/img.rs +++ b/src/io/img.rs @@ -72,6 +72,45 @@ pub fn deserialize_img(path: impl AsRef, bytes: &[u8]) -> Result, bytes: &[u8]) -> Result { + use cgmath::num_traits::ToPrimitive; + + let name = path + .as_ref() + .to_str() + .filter(|s| !s.starts_with("data:")) + .unwrap_or("default") + .to_owned(); + let tree = resvg::usvg::Tree::from_data(bytes, &resvg::usvg::Options::default())?; + // TODO: should we have more error checking here? + let (width, height) = ( + tree.size().width().to_u32().unwrap(), + tree.size().height().to_u32().unwrap(), + ); + let mut pixmap = resvg::tiny_skia::Pixmap::new(width, height).unwrap(); + resvg::render( + &tree, + resvg::tiny_skia::Transform::default(), + &mut pixmap.as_mut(), + ); + + // process the data to our desired RGBAU8 format + let texture_data: Vec<[u8; 4]> = pixmap + .pixels() + .iter() + .map(|pixel| [pixel.red(), pixel.green(), pixel.blue(), pixel.alpha()]) + .collect(); + + Ok(Texture2D { + name, + data: TextureData::RgbaU8(texture_data), + width, + height, + ..Default::default() + }) +} + pub fn serialize_img(tex: &Texture2D, path: &Path) -> Result { #![allow(unreachable_code)] #![allow(unused_variables)] @@ -266,4 +305,21 @@ mod test { assert_eq!(tex.width, 1024); assert_eq!(tex.height, 512); } + + #[cfg(feature = "svg")] + #[test] + pub fn svg() { + let tex: crate::Texture2D = crate::io::load_and_deserialize("test_data/test.svg").unwrap(); + if let crate::TextureData::RgbaU8(data) = tex.data { + assert_eq!(data[0], [0, 0, 0, 0]); + assert_eq!(data[25036], [0, 51, 255, 255]); + assert_eq!(data[20062], [255, 0, 0, 255]); + assert_eq!(data[58095], [0, 255, 0, 255]); + } else { + panic!("Wrong texture data"); + } + + assert_eq!(tex.width, 320); + assert_eq!(tex.height, 240); + } } diff --git a/src/lib.rs b/src/lib.rs index 3c7bfe4..7df2c3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -218,6 +218,11 @@ pub enum Error { #[cfg(feature = "image")] #[error("error while parsing an image file")] Image(#[from] image::ImageError), + + #[cfg(feature = "svg")] + #[error("error while parsing svg file")] + Svg(#[from] resvg::usvg::Error), + #[cfg(feature = "obj")] #[error("error while parsing an .obj file")] Obj(#[from] wavefront_obj::ParseError), diff --git a/test_data/test.svg b/test_data/test.svg new file mode 100644 index 0000000..3489d72 --- /dev/null +++ b/test_data/test.svg @@ -0,0 +1,10 @@ + + + + + Ellipses + + + + +