From ae009158cc322b69403a2512ac51582062029c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 31 Mar 2020 00:39:18 +0200 Subject: [PATCH] Implement `svg::Handle::from_memory` Useful if you already have your SVG data in memory. --- examples/svg/Cargo.toml | 1 - examples/svg/src/main.rs | 7 +--- native/src/widget/svg.rs | 80 ++++++++++++++++++++++++++++++---------- wgpu/src/image/vector.rs | 16 ++++++-- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/examples/svg/Cargo.toml b/examples/svg/Cargo.toml index 161ee6a8..d8f83ac2 100644 --- a/examples/svg/Cargo.toml +++ b/examples/svg/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../..", features = ["svg"] } -env_logger = "0.7" diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 811fdfb5..80e28cf8 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -1,19 +1,16 @@ use iced::{Column, Container, Element, Length, Sandbox, Settings, Svg}; pub fn main() { - env_logger::init(); - Tiger::run(Settings::default()) } -#[derive(Default)] struct Tiger; impl Sandbox for Tiger { type Message = (); fn new() -> Self { - Self::default() + Tiger } fn title(&self) -> String { @@ -24,7 +21,7 @@ impl Sandbox for Tiger { fn view(&mut self) -> Element<()> { let content = Column::new().padding(20).push( - Svg::new(format!( + Svg::from_path(format!( "{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR") )) diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 4a227f3d..114d5e41 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -2,8 +2,9 @@ use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; use std::{ - hash::Hash, - path::{Path, PathBuf}, + hash::{Hash, Hasher as _}, + path::PathBuf, + sync::Arc, }; /// A vector graphics image. @@ -34,6 +35,14 @@ impl Svg { } } + /// Creates a new [`Svg`] that will display the contents of the file at the + /// provided path. + /// + /// [`Svg`]: struct.Svg.html + pub fn from_path(path: impl Into) -> Self { + Self::new(Handle::from_path(path)) + } + /// Sets the width of the [`Svg`]. /// /// [`Svg`]: struct.Svg.html @@ -101,6 +110,7 @@ where fn hash_layout(&self, state: &mut Hasher) { std::any::TypeId::of::().hash(state); + self.handle.hash(state); self.width.hash(state); self.height.hash(state); } @@ -112,7 +122,7 @@ where #[derive(Debug, Clone)] pub struct Handle { id: u64, - path: PathBuf, + data: Arc, } impl Handle { @@ -120,17 +130,28 @@ impl Handle { /// path. /// /// [`Handle`]: struct.Handle.html - pub fn from_path>(path: T) -> Handle { - use std::hash::Hasher as _; + pub fn from_path(path: impl Into) -> Handle { + Self::from_data(Data::Path(path.into())) + } - let path = path.into(); + /// Creates an SVG [`Handle`] from raw bytes containing either an SVG string + /// or gzip compressed data. + /// + /// This is useful if you already have your SVG data in-memory, maybe + /// because you downloaded or generated it procedurally. + /// + /// [`Handle`]: struct.Handle.html + pub fn from_memory(bytes: impl Into>) -> Handle { + Self::from_data(Data::Bytes(bytes.into())) + } + fn from_data(data: Data) -> Handle { let mut hasher = Hasher::default(); - path.hash(&mut hasher); + data.hash(&mut hasher); Handle { id: hasher.finish(), - path, + data: Arc::new(data), } } @@ -141,20 +162,40 @@ impl Handle { self.id } - /// Returns a reference to the path of the [`Handle`]. + /// Returns a reference to the SVG [`Data`]. /// - /// [`Handle`]: enum.Handle.html - pub fn path(&self) -> &Path { - &self.path + /// [`Data`]: enum.Data.html + pub fn data(&self) -> &Data { + &self.data } } -impl From for Handle -where - T: Into, -{ - fn from(path: T) -> Handle { - Handle::from_path(path) +impl Hash for Handle { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +/// The data of an [`Svg`]. +/// +/// [`Svg`]: struct.Svg.html +#[derive(Clone, Hash)] +pub enum Data { + /// File data + Path(PathBuf), + + /// In-memory data + /// + /// Can contain an SVG string or a gzip compressed data. + Bytes(Vec), +} + +impl std::fmt::Debug for Data { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Data::Path(path) => write!(f, "Path({:?})", path), + Data::Bytes(_) => write!(f, "Bytes(...)"), + } } } @@ -166,9 +207,10 @@ where /// [`Svg`]: struct.Svg.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { - /// Returns the default dimensions of an [`Svg`] located on the given path. + /// Returns the default dimensions of an [`Svg`] for the given [`Handle`]. /// /// [`Svg`]: struct.Svg.html + /// [`Handle`]: struct.Handle.html fn dimensions(&self, handle: &Handle) -> (u32, u32); /// Draws an [`Svg`]. diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs index bae0f82f..b6776827 100644 --- a/wgpu/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -45,9 +45,19 @@ impl Cache { let opt = resvg::Options::default(); - let svg = match resvg::usvg::Tree::from_file(handle.path(), &opt.usvg) { - Ok(tree) => Svg::Loaded(tree), - Err(_) => Svg::NotFound, + let svg = match handle.data() { + svg::Data::Path(path) => { + match resvg::usvg::Tree::from_file(path, &opt.usvg) { + Ok(tree) => Svg::Loaded(tree), + Err(_) => Svg::NotFound, + } + } + svg::Data::Bytes(bytes) => { + match resvg::usvg::Tree::from_data(&bytes, &opt.usvg) { + Ok(tree) => Svg::Loaded(tree), + Err(_) => Svg::NotFound, + } + } }; let _ = self.svgs.insert(handle.id(), svg);