Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SVG format #168

Merged
merged 7 commits into from
Jan 11, 2021
Merged

Support SVG format #168

merged 7 commits into from
Jan 11, 2021

Conversation

carrascomj
Copy link
Contributor

Closes #85

Description

Support SVG format with resvg, since the blocking dep harfbuzz_rs was replaced with a pure rust crate - rustybuzz - a while ago (see https://github.com/RazrFalcon/resvg/blob/master/CHANGELOG.md).

Implementation

  • A new cargo feature - svg was added (I mirrored the avif support but I don't know if this should be a feature).
  • resvg, usvg and tiny-skia were added as optional dependencies.
  • Svgs are parsed (usvg), rendered (resvg) and encoded as PNGs (tiny-skia).
  • The svg format is deduced from the file extension, which I don't think is the preferred way for emulsion, but I am not sure about using the "header" of an SVG to do it (suggestions?).

Copy link
Owner

@ArturKovacs ArturKovacs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution, svg support is a great addition, which I've been wanting to do for a long time.

I would like to point out that optimally the SVG should be re-rasterized ("re-rendered") when the user zooms in, so that you can zoom right into the tiniest detail and the image would remain sharp and not pixelated. I gave it bit of thought and implementing that properly is a much more difficult and time consuming task so the approch that you took in this PR should be good enough as a start.

I did leave some remarks that I'd like you to address. I'm happy to give further guidance if needed.

Cargo.toml Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
src/image_cache/image_loader.rs Outdated Show resolved Hide resolved
src/image_cache/image_loader.rs Outdated Show resolved Hide resolved
src/image_cache/image_loader.rs Outdated Show resolved Hide resolved
src/image_cache/image_loader.rs Outdated Show resolved Hide resolved
Comment on lines 170 to 172
let mut pixmap = tiny_skia::Pixmap::new(width, height).unwrap();
resvg::render(&rtree, usvg::FitTo::Zoom(zoom as f32), pixmap.as_mut()).unwrap();
Ok(image::RgbaImage::from_raw(width, height, pixmap.data().to_vec()).unwrap())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is exactly what I was thinking about. 😊

There is one more improvement that can be made here. pixmap.data() only gives you a reference to the pixel array within pixmap, then to_vec takes the reference and creates a new array, copying all the data, then you throw away the entire array contained within pixmap. As you can see there's a copy which we don't really need. We can avoid creating a copy by moving the pixmap's internal data into the from_raw function. Unfortunately the data inside Pixmap is not pub so we can't reach it directly. But there's a function for Pixmap which was created exatcly for this purpose, as shown below. (Notice that this function takes self instead of &self, this means that self is moved)

    /// Consumes the internal data.
    ///
    /// Bytes are ordered as RGBA.
    pub fn take(self) -> Vec<u8> {
        self.data
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed pointers, they really helped. It should be fine now.

Copy link
Owner

@ArturKovacs ArturKovacs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost perfect, just one last thing.

pub fn load_svg(path: &std::path::Path) -> Result<image::RgbaImage> {
let opt = usvg::Options::default();
let rtree = usvg::Tree::from_file(path, &opt)?;
let (width, height) = (rtree.svg_node().size.width(), rtree.svg_node().size.height());
Copy link
Owner

@ArturKovacs ArturKovacs Jan 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: What I suggested earlier does not compile 🤦‍♂️

May I suggest

let size = rtree.svg_node().size;
let (width, height) = (size.width(), size.height());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed. I believe that kind of pattern matching on let assignments is still WIP.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually not. The problem was that the fields of Size are not public. The reason for that is beyond me.

@ArturKovacs ArturKovacs merged commit 8356939 into ArturKovacs:master Jan 11, 2021
@ArturKovacs
Copy link
Owner

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add SVG support
2 participants