Skip to content

Commit

Permalink
feat: add a way to provide customable scaling to SVG rasterization (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bluskript authored Nov 7, 2022
1 parent b3ab47a commit 3805a32
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 19 deletions.
1 change: 1 addition & 0 deletions crates/egui_extras/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to the `egui_extras` integration will be noted in this file.

## Unreleased
* Added `TableBuilder::vertical_scroll_offset`: method to set vertical scroll offset position for a table ([#1946](https://github.com/emilk/egui/pull/1946)).
* Added `RetainedImage::from_svg_bytes_with_size` to be able to specify a size for SVGs to be rasterized at.

## 0.19.0 - 2022-08-20
* MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)).
Expand Down
72 changes: 55 additions & 17 deletions crates/egui_extras/src/image.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use egui::{mutex::Mutex, TextureFilter, TextureOptions};

#[cfg(feature = "svg")]
pub use usvg::FitTo;

/// An image to be shown in egui.
///
/// Load once, and save somewhere in your app state.
Expand Down Expand Up @@ -52,10 +55,7 @@ impl RetainedImage {
/// On invalid image
#[cfg(feature = "svg")]
pub fn from_svg_bytes(debug_name: impl Into<String>, svg_bytes: &[u8]) -> Result<Self, String> {
Ok(Self::from_color_image(
debug_name,
load_svg_bytes(svg_bytes)?,
))
Self::from_svg_bytes_with_size(debug_name, svg_bytes, FitTo::Original)
}

/// Pass in the str of an SVG that you've loaded.
Expand All @@ -67,6 +67,23 @@ impl RetainedImage {
Self::from_svg_bytes(debug_name, svg_str.as_bytes())
}

/// Pass in the bytes of an SVG that you've loaded
/// and the scaling option to resize the SVG with
///
/// # Errors
/// On invalid image
#[cfg(feature = "svg")]
pub fn from_svg_bytes_with_size(
debug_name: impl Into<String>,
svg_bytes: &[u8],
size: FitTo,
) -> Result<Self, String> {
Ok(Self::from_color_image(
debug_name,
load_svg_bytes_with_size(svg_bytes, size)?,
))
}

/// Set the texture filters to use for the image.
///
/// **Note:** If the texture has already been uploaded to the GPU, this will require
Expand Down Expand Up @@ -200,29 +217,50 @@ pub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, String>
/// On invalid image
#[cfg(feature = "svg")]
pub fn load_svg_bytes(svg_bytes: &[u8]) -> Result<egui::ColorImage, String> {
load_svg_bytes_with_size(svg_bytes, FitTo::Original)
}

/// Load an SVG and rasterize it into an egui image with a scaling parameter.
///
/// Requires the "svg" feature.
///
/// # Errors
/// On invalid image
#[cfg(feature = "svg")]
pub fn load_svg_bytes_with_size(
svg_bytes: &[u8],
fit_to: FitTo,
) -> Result<egui::ColorImage, String> {
let mut opt = usvg::Options::default();
opt.fontdb.load_system_fonts();

let rtree = usvg::Tree::from_data(svg_bytes, &opt.to_ref()).map_err(|err| err.to_string())?;

let pixmap_size = rtree.svg_node().size.to_screen_size();
let [w, h] = [pixmap_size.width(), pixmap_size.height()];
let [w, h] = match fit_to {
FitTo::Original => [pixmap_size.width(), pixmap_size.height()],
FitTo::Size(w, h) => [w, h],
FitTo::Height(h) => [
(pixmap_size.width() as f32 * (h as f32 / pixmap_size.height() as f32)) as u32,
h,
],
FitTo::Width(w) => [
w,
(pixmap_size.height() as f32 * (w as f32 / pixmap_size.width() as f32)) as u32,
],
FitTo::Zoom(z) => [
(pixmap_size.width() as f32 * z) as u32,
(pixmap_size.height() as f32 * z) as u32,
],
};

let mut pixmap = tiny_skia::Pixmap::new(w, h)
.ok_or_else(|| format!("Failed to create SVG Pixmap of size {}x{}", w, h))?;

resvg::render(
&rtree,
usvg::FitTo::Original,
Default::default(),
pixmap.as_mut(),
)
.ok_or_else(|| "Failed to render SVG".to_owned())?;

let image = egui::ColorImage::from_rgba_unmultiplied(
[pixmap.width() as _, pixmap.height() as _],
pixmap.data(),
);
resvg::render(&rtree, fit_to, Default::default(), pixmap.as_mut())
.ok_or_else(|| "Failed to render SVG".to_owned())?;

let image = egui::ColorImage::from_rgba_unmultiplied([w as _, h as _], pixmap.data());

Ok(image)
}
5 changes: 3 additions & 2 deletions examples/svg/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ struct MyApp {
impl Default for MyApp {
fn default() -> Self {
Self {
svg_image: egui_extras::RetainedImage::from_svg_bytes(
svg_image: egui_extras::RetainedImage::from_svg_bytes_with_size(
"rustacean-flat-happy.svg",
include_bytes!("rustacean-flat-happy.svg"),
egui_extras::image::FitTo::Original,
)
.unwrap(),
}
Expand All @@ -43,7 +44,7 @@ impl eframe::App for MyApp {
ui.separator();

let max_size = ui.available_size();
self.svg_image.show_max_size(ui, max_size);
self.svg_image.show_size(ui, max_size);
});
}
}

0 comments on commit 3805a32

Please sign in to comment.