diff options
Diffstat (limited to 'core/src/image.rs')
-rw-r--r-- | core/src/image.rs | 216 |
1 files changed, 98 insertions, 118 deletions
diff --git a/core/src/image.rs b/core/src/image.rs index e5fdcd83..82ecdd0f 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,15 +1,45 @@ //! Load and draw raster graphics. -use crate::{Hasher, Rectangle, Size}; +pub use bytes::Bytes; -use std::hash::{Hash, Hasher as _}; -use std::path::PathBuf; -use std::sync::Arc; +use crate::{Radians, Rectangle, Size}; + +use rustc_hash::FxHasher; +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; /// A handle of some image data. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Handle { - id: u64, - data: Data, +#[derive(Clone, PartialEq, Eq)] +pub enum Handle { + /// A file handle. The image data will be read + /// from the file path. + /// + /// Use [`from_path`] to create this variant. + /// + /// [`from_path`]: Self::from_path + Path(Id, PathBuf), + + /// A handle pointing to some encoded image bytes in-memory. + /// + /// Use [`from_bytes`] to create this variant. + /// + /// [`from_bytes`]: Self::from_bytes + Bytes(Id, Bytes), + + /// A handle pointing to decoded image pixels in RGBA format. + /// + /// Use [`from_rgba`] to create this variant. + /// + /// [`from_rgba`]: Self::from_rgba + Rgba { + /// The id of this handle. + id: Id, + /// The width of the image. + width: u32, + /// The height of the image. + height: u32, + /// The pixels. + pixels: Bytes, + }, } impl Handle { @@ -17,56 +47,48 @@ impl Handle { /// /// Makes an educated guess about the image format by examining the data in the file. pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle { - Self::from_data(Data::Path(path.into())) - } + let path = path.into(); - /// Creates an image [`Handle`] containing the image pixels directly. This - /// function expects the input data to be provided as a `Vec<u8>` of RGBA - /// pixels. - /// - /// This is useful if you have already decoded your image. - pub fn from_pixels( - width: u32, - height: u32, - pixels: impl AsRef<[u8]> + Send + Sync + 'static, - ) -> Handle { - Self::from_data(Data::Rgba { - width, - height, - pixels: Bytes::new(pixels), - }) + Self::Path(Id::path(&path), path) } - /// Creates an image [`Handle`] containing the image data directly. + /// Creates an image [`Handle`] containing the encoded image data directly. /// /// Makes an educated guess about the image format by examining the given data. /// /// This is useful if you already have your image loaded in-memory, maybe /// because you downloaded or generated it procedurally. - pub fn from_memory( - bytes: impl AsRef<[u8]> + Send + Sync + 'static, - ) -> Handle { - Self::from_data(Data::Bytes(Bytes::new(bytes))) + pub fn from_bytes(bytes: impl Into<Bytes>) -> Handle { + Self::Bytes(Id::unique(), bytes.into()) } - fn from_data(data: Data) -> Handle { - let mut hasher = Hasher::default(); - data.hash(&mut hasher); - - Handle { - id: hasher.finish(), - data, + /// Creates an image [`Handle`] containing the decoded image pixels directly. + /// + /// This function expects the pixel data to be provided as a collection of [`Bytes`] + /// of RGBA pixels. Therefore, the length of the pixel data should always be + /// `width * height * 4`. + /// + /// This is useful if you have already decoded your image. + pub fn from_rgba( + width: u32, + height: u32, + pixels: impl Into<Bytes>, + ) -> Handle { + Self::Rgba { + id: Id::unique(), + width, + height, + pixels: pixels.into(), } } /// Returns the unique identifier of the [`Handle`]. - pub fn id(&self) -> u64 { - self.id - } - - /// Returns a reference to the image [`Data`]. - pub fn data(&self) -> &Data { - &self.data + pub fn id(&self) -> Id { + match self { + Handle::Path(id, _) + | Handle::Bytes(id, _) + | Handle::Rgba { id, .. } => *id, + } } } @@ -79,90 +101,46 @@ where } } -impl Hash for Handle { - fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - self.id.hash(state); - } -} - -/// A wrapper around raw image data. -/// -/// It behaves like a `&[u8]`. -#[derive(Clone)] -pub struct Bytes(Arc<dyn AsRef<[u8]> + Send + Sync + 'static>); - -impl Bytes { - /// Creates new [`Bytes`] around `data`. - pub fn new(data: impl AsRef<[u8]> + Send + Sync + 'static) -> Self { - Self(Arc::new(data)) - } -} - -impl std::fmt::Debug for Bytes { +impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.as_ref().as_ref().fmt(f) + match self { + Self::Path(_, path) => write!(f, "Path({path:?})"), + Self::Bytes(_, _) => write!(f, "Bytes(...)"), + Self::Rgba { width, height, .. } => { + write!(f, "Pixels({width} * {height})") + } + } } } -impl std::hash::Hash for Bytes { - fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - self.0.as_ref().as_ref().hash(state); - } -} +/// The unique identifier of some [`Handle`] data. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id(_Id); -impl PartialEq for Bytes { - fn eq(&self, other: &Self) -> bool { - let a = self.as_ref(); - let b = other.as_ref(); - core::ptr::eq(a, b) || a == b - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum _Id { + Unique(u64), + Hash(u64), } -impl Eq for Bytes {} - -impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref().as_ref() - } -} +impl Id { + fn unique() -> Self { + use std::sync::atomic::{self, AtomicU64}; -impl std::ops::Deref for Bytes { - type Target = [u8]; + static NEXT_ID: AtomicU64 = AtomicU64::new(0); - fn deref(&self) -> &[u8] { - self.0.as_ref().as_ref() + Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed))) } -} -/// The data of a raster image. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Data { - /// File data - Path(PathBuf), + fn path(path: impl AsRef<Path>) -> Self { + let hash = { + let mut hasher = FxHasher::default(); + path.as_ref().hash(&mut hasher); - /// In-memory data - Bytes(Bytes), - - /// Decoded image pixels in RGBA format. - Rgba { - /// The width of the image. - width: u32, - /// The height of the image. - height: u32, - /// The pixels. - pixels: Bytes, - }, -} + hasher.finish() + }; -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(...)"), - Data::Rgba { width, height, .. } => { - write!(f, "Pixels({width} * {height})") - } - } + Self(_Id::Hash(hash)) } } @@ -183,17 +161,19 @@ pub trait Renderer: crate::Renderer { /// The image Handle to be displayed. Iced exposes its own default implementation of a [`Handle`] /// /// [`Handle`]: Self::Handle - type Handle: Clone + Hash; + type Handle: Clone; /// Returns the dimensions of an image for the given [`Handle`]. - fn dimensions(&self, handle: &Self::Handle) -> Size<u32>; + fn measure_image(&self, handle: &Self::Handle) -> Size<u32>; /// Draws an image with the given [`Handle`] and inside the provided /// `bounds`. - fn draw( + fn draw_image( &mut self, handle: Self::Handle, filter_method: FilterMethod, bounds: Rectangle, + rotation: Radians, + opacity: f32, ); } |