mirror of https://github.com/emilk/egui.git
Emil Ernerfeldt
3 years ago
committed by
GitHub
8 changed files with 411 additions and 11 deletions
After Width: | Height: | Size: 7.3 KiB |
@ -0,0 +1,108 @@ |
|||
//! A good way of displaying an SVG image in egui.
|
|||
//!
|
|||
//! Requires the dependencies `resvg`, `tiny-skia`, `usvg`
|
|||
use eframe::{egui, epi}; |
|||
|
|||
/// Load an SVG and rasterize it into an egui image.
|
|||
fn load_svg_data(svg_data: &[u8]) -> Result<egui::ColorImage, String> { |
|||
let mut opt = usvg::Options::default(); |
|||
opt.fontdb.load_system_fonts(); |
|||
|
|||
let rtree = usvg::Tree::from_data(svg_data, &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 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, |
|||
tiny_skia::Transform::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(), |
|||
); |
|||
|
|||
Ok(image) |
|||
} |
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
/// An SVG image to be shown in egui
|
|||
struct SvgImage { |
|||
image: egui::ColorImage, |
|||
texture: Option<egui::TextureHandle>, |
|||
} |
|||
|
|||
impl SvgImage { |
|||
/// Pass itn the bytes of an SVG that you've loaded from disk
|
|||
pub fn from_svg_data(bytes: &[u8]) -> Result<Self, String> { |
|||
Ok(Self { |
|||
image: load_svg_data(bytes)?, |
|||
texture: None, |
|||
}) |
|||
} |
|||
|
|||
pub fn show_max_size(&mut self, ui: &mut egui::Ui, max_size: egui::Vec2) -> egui::Response { |
|||
let mut desired_size = egui::vec2(self.image.width() as _, self.image.height() as _); |
|||
desired_size *= (max_size.x / desired_size.x).min(1.0); |
|||
desired_size *= (max_size.y / desired_size.y).min(1.0); |
|||
self.show_size(ui, desired_size) |
|||
} |
|||
|
|||
pub fn show_size(&mut self, ui: &mut egui::Ui, desired_size: egui::Vec2) -> egui::Response { |
|||
// We need to convert the SVG to a texture to display it:
|
|||
// Future improvement: tell backend to do mip-mapping of the image to
|
|||
// make it look smoother when downsized.
|
|||
let svg_texture = self |
|||
.texture |
|||
.get_or_insert_with(|| ui.ctx().load_texture("svg", self.image.clone())); |
|||
ui.image(svg_texture, desired_size) |
|||
} |
|||
} |
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
|
|||
struct MyApp { |
|||
svg_image: SvgImage, |
|||
} |
|||
|
|||
impl Default for MyApp { |
|||
fn default() -> Self { |
|||
Self { |
|||
svg_image: SvgImage::from_svg_data(include_bytes!("rustacean-flat-happy.svg")).unwrap(), |
|||
} |
|||
} |
|||
} |
|||
|
|||
impl epi::App for MyApp { |
|||
fn name(&self) -> &str { |
|||
"svg example" |
|||
} |
|||
|
|||
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) { |
|||
egui::CentralPanel::default().show(ctx, |ui| { |
|||
ui.heading("SVG example"); |
|||
ui.label("The SVG is rasterized and displayed as a texture."); |
|||
|
|||
ui.separator(); |
|||
|
|||
let max_size = ui.available_size(); |
|||
self.svg_image.show_max_size(ui, max_size); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
fn main() { |
|||
let options = eframe::NativeOptions { |
|||
initial_window_size: Some(egui::vec2(1000.0, 700.0)), |
|||
..Default::default() |
|||
}; |
|||
eframe::run_native(Box::new(MyApp::default()), options); |
|||
} |
Loading…
Reference in new issue