Skip to content

Commit

Permalink
SVG deserialization support (#37)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
themkat authored Sep 2, 2024
1 parent 0289b46 commit 319c6f0
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 11 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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"] }
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 18 additions & 9 deletions src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,28 @@ use std::path::{Path, PathBuf};
impl Deserialize for crate::Texture2D {
fn deserialize(path: impl AsRef<std::path::Path>, raw_assets: &mut RawAssets) -> Result<Self> {
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)
}
}
}

Expand Down
56 changes: 56 additions & 0 deletions src/io/img.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,45 @@ pub fn deserialize_img(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Texture2D
})
}

#[cfg(feature = "svg")]
pub fn deserialize_svg(path: impl AsRef<Path>, bytes: &[u8]) -> Result<Texture2D> {
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<RawAssets> {
#![allow(unreachable_code)]
#![allow(unused_variables)]
Expand Down Expand Up @@ -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);
}
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
10 changes: 10 additions & 0 deletions test_data/test.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 319c6f0

Please sign in to comment.