diff options
author | 2021-03-21 14:36:06 +1100 | |
---|---|---|
committer | 2021-03-21 14:36:06 +1100 | |
commit | 1d85b6e887761ae885bec3f0b5405bf902ef8b64 (patch) | |
tree | 5a1595e91a8f7f9c3fff91b0a1d5528249abef04 /web/src/widget | |
parent | 0333a8daff6db989adc6035a4c09df171a86f6fe (diff) | |
download | iced-1d85b6e887761ae885bec3f0b5405bf902ef8b64.tar.gz iced-1d85b6e887761ae885bec3f0b5405bf902ef8b64.tar.bz2 iced-1d85b6e887761ae885bec3f0b5405bf902ef8b64.zip |
feat(web): Support in-memory image data
I had to create two methods which basically do the same thing, `from_memory` and `from_slice`, but `from_memory` takes ownership of the bytes to be compatible with `iced_native`.
Also, `Data` is incompatible, because if I stored the bytes in `Data` and created a new object URL every render, it would have caused a memory leak because bumpalo doesn't call destructors and there'd be no way to call URL.revokeObjectUrl on it. It's also more efficient this way.
Diffstat (limited to 'web/src/widget')
-rw-r--r-- | web/src/widget/image.rs | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/web/src/widget/image.rs b/web/src/widget/image.rs index 05c89ea5..545e7e28 100644 --- a/web/src/widget/image.rs +++ b/web/src/widget/image.rs @@ -2,11 +2,15 @@ use crate::{Bus, Css, Element, Hasher, Length, Widget}; use dodrio::bumpalo; +use js_sys::Array; +use js_sys::Uint8Array; use std::{ hash::{Hash, Hasher as _}, path::PathBuf, sync::Arc, }; +use web_sys::Blob; +use web_sys::Url; /// A frame that displays an image while keeping aspect ratio. /// @@ -75,6 +79,7 @@ impl<Message> Widget<Message> for Image { let src = String::from_str_in( match self.handle.data.as_ref() { Data::Path(path) => path.to_str().unwrap_or(""), + Data::ObjectUrl(url) => &url, }, bump, ) @@ -122,6 +127,27 @@ impl Handle { Self::from_data(Data::Path(path.into())) } + /// Creates an image [`Handle`] containing the image data directly. + /// + /// NOTE: this unnecessarily takes ownership of the data to be compaticle with `iced_native`. + /// If you're only using `iced_web`, you should use `from_slice` instead. + pub fn from_memory(bytes: Vec<u8>) -> Handle { + Handle::from_slice(bytes.as_slice()) + } + + /// Creates an image [`Handle`] containing the image data directly. + pub fn from_slice(bytes: &[u8]) -> Handle { + // This copies the memory twice (once in Uint8Array::from and once in Blob::new), + // but unsafe code is needed not to do that and #[forbid(unsafe_code)] is on. + let blob = Blob::new_with_u8_array_sequence(&Array::of1( + &Uint8Array::from(bytes), + )) + .unwrap(); + Self::from_data(Data::ObjectUrl( + Url::create_object_url_with_blob(&blob).unwrap(), + )) + } + fn from_data(data: Data) -> Handle { let mut hasher = Hasher::default(); data.hash(&mut hasher); @@ -160,12 +186,25 @@ impl From<&str> for Handle { pub enum Data { /// A remote image Path(PathBuf), + + /// An Object URL pointing to some image data. + ObjectUrl(String), } 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::ObjectUrl(url) => write!(f, "ObjectUrl({:?})", url), + } + } +} + +impl Drop for Data { + fn drop(&mut self) { + match self { + Data::ObjectUrl(url) => Url::revoke_object_url(&url).unwrap(), + _ => {} } } } |