From 8d27af24a76d9792e22b3380e11b846fd5533805 Mon Sep 17 00:00:00 2001 From: Bajix Date: Wed, 27 Mar 2024 12:54:01 -0700 Subject: Utilize bytes::Bytes for images --- core/src/image.rs | 62 ++++++------------------------------------------------- 1 file changed, 6 insertions(+), 56 deletions(-) (limited to 'core/src') diff --git a/core/src/image.rs b/core/src/image.rs index dc74e5c1..7316ede7 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,10 +1,11 @@ //! Load and draw raster graphics. +pub use bytes::Bytes; + use crate::{Rectangle, Size}; use rustc_hash::FxHasher; use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; -use std::sync::Arc; /// A handle of some image data. #[derive(Debug, Clone, PartialEq, Eq)] @@ -29,12 +30,12 @@ impl Handle { pub fn from_pixels( width: u32, height: u32, - pixels: impl AsRef<[u8]> + Send + Sync + 'static, + pixels: impl Into, ) -> Handle { Self::from_data(Data::Rgba { width, height, - pixels: Bytes::new(pixels), + pixels: pixels.into(), }) } @@ -44,10 +45,8 @@ impl Handle { /// /// 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_memory(bytes: impl Into) -> Handle { + Self::from_data(Data::Bytes(bytes.into())) } fn from_data(data: Data) -> Handle { @@ -86,55 +85,6 @@ impl Hash for Handle { } } -/// A wrapper around raw image data. -/// -/// It behaves like a `&[u8]`. -#[derive(Clone)] -pub struct Bytes(Arc + 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 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.as_ref().as_ref().fmt(f) - } -} - -impl std::hash::Hash for Bytes { - fn hash(&self, state: &mut H) { - self.0.as_ref().as_ref().hash(state); - } -} - -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 - } -} - -impl Eq for Bytes {} - -impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref().as_ref() - } -} - -impl std::ops::Deref for Bytes { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.0.as_ref().as_ref() - } -} - /// The data of a raster image. #[derive(Clone, PartialEq, Eq, Hash)] pub enum Data { -- cgit From 45254ab88c6ca76759523069c2fb8734de626f02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 00:55:49 +0200 Subject: Use `Bytes` as the `Container` of `ImageBuffer` Since we don't need to mutate images once loaded, we avoid unnecessary extra allocations. --- core/src/image.rs | 91 +++++++++++++++++++++++-------------------------------- 1 file changed, 38 insertions(+), 53 deletions(-) (limited to 'core/src') diff --git a/core/src/image.rs b/core/src/image.rs index 7316ede7..5b31fbcf 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -4,14 +4,27 @@ pub use bytes::Bytes; use crate::{Rectangle, Size}; use rustc_hash::FxHasher; -use std::hash::{Hash, Hasher as _}; +use std::hash::{Hash, Hasher}; use std::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 { + /// File data + Path(PathBuf), + + /// 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, + }, } impl Handle { @@ -19,7 +32,7 @@ impl Handle { /// /// Makes an educated guess about the image format by examining the data in the file. pub fn from_path>(path: T) -> Handle { - Self::from_data(Data::Path(path.into())) + Self::Path(path.into()) } /// Creates an image [`Handle`] containing the image pixels directly. This @@ -32,11 +45,11 @@ impl Handle { height: u32, pixels: impl Into, ) -> Handle { - Self::from_data(Data::Rgba { + Self::Rgba { width, height, pixels: pixels.into(), - }) + } } /// Creates an image [`Handle`] containing the image data directly. @@ -46,27 +59,25 @@ impl Handle { /// 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 Into) -> Handle { - Self::from_data(Data::Bytes(bytes.into())) - } - - fn from_data(data: Data) -> Handle { - let mut hasher = FxHasher::default(); - data.hash(&mut hasher); - - Handle { - id: hasher.finish(), - data, - } + Self::Bytes(bytes.into()) } /// Returns the unique identifier of the [`Handle`]. pub fn id(&self) -> u64 { - self.id + let mut hasher = FxHasher::default(); + self.hash(&mut hasher); + + hasher.finish() } +} - /// Returns a reference to the image [`Data`]. - pub fn data(&self) -> &Data { - &self.data +impl Hash for Handle { + fn hash(&self, state: &mut H) { + match self { + Self::Path(path) => path.hash(state), + Self::Bytes(bytes) => bytes.as_ptr().hash(state), + Self::Rgba { pixels, .. } => pixels.as_ptr().hash(state), + } } } @@ -79,38 +90,12 @@ where } } -impl Hash for Handle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -/// The data of a raster image. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Data { - /// File data - Path(PathBuf), - - /// 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, - }, -} - -impl std::fmt::Debug for Data { +impl std::fmt::Debug for Handle { 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, .. } => { + Self::Path(path) => write!(f, "Path({path:?})"), + Self::Bytes(_) => write!(f, "Bytes(...)"), + Self::Rgba { width, height, .. } => { write!(f, "Pixels({width} * {height})") } } -- cgit From b52c7bb610f593fffc624d461dca17ac50c81626 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:39:43 +0200 Subject: Use an opaque `Id` type for `image::Handle` Hashing pointers is a terrible idea. --- core/src/image.rs | 68 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 21 deletions(-) (limited to 'core/src') diff --git a/core/src/image.rs b/core/src/image.rs index 5b31fbcf..a0e40787 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -5,19 +5,21 @@ use crate::{Rectangle, Size}; use rustc_hash::FxHasher; use std::hash::{Hash, Hasher}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// A handle of some image data. #[derive(Clone, PartialEq, Eq)] pub enum Handle { /// File data - Path(PathBuf), + Path(Id, PathBuf), /// In-memory data - Bytes(Bytes), + Bytes(Id, Bytes), /// Decoded image pixels in RGBA format. Rgba { + /// The id of this handle. + id: Id, /// The width of the image. width: u32, /// The height of the image. @@ -32,7 +34,9 @@ impl Handle { /// /// Makes an educated guess about the image format by examining the data in the file. pub fn from_path>(path: T) -> Handle { - Self::Path(path.into()) + let path = path.into(); + + Self::Path(Id::path(&path), path) } /// Creates an image [`Handle`] containing the image pixels directly. This @@ -46,6 +50,7 @@ impl Handle { pixels: impl Into, ) -> Handle { Self::Rgba { + id: Id::unique(), width, height, pixels: pixels.into(), @@ -59,24 +64,15 @@ impl Handle { /// 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 Into) -> Handle { - Self::Bytes(bytes.into()) + Self::Bytes(Id::unique(), bytes.into()) } /// Returns the unique identifier of the [`Handle`]. - pub fn id(&self) -> u64 { - let mut hasher = FxHasher::default(); - self.hash(&mut hasher); - - hasher.finish() - } -} - -impl Hash for Handle { - fn hash(&self, state: &mut H) { + pub fn id(&self) -> Id { match self { - Self::Path(path) => path.hash(state), - Self::Bytes(bytes) => bytes.as_ptr().hash(state), - Self::Rgba { pixels, .. } => pixels.as_ptr().hash(state), + Handle::Path(id, _) + | Handle::Bytes(id, _) + | Handle::Rgba { id, .. } => *id, } } } @@ -93,8 +89,8 @@ where impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Path(path) => write!(f, "Path({path:?})"), - Self::Bytes(_) => write!(f, "Bytes(...)"), + Self::Path(_, path) => write!(f, "Path({path:?})"), + Self::Bytes(_, _) => write!(f, "Bytes(...)"), Self::Rgba { width, height, .. } => { write!(f, "Pixels({width} * {height})") } @@ -102,6 +98,36 @@ impl std::fmt::Debug for Handle { } } +/// The unique identifier of some [`Handle`] data. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Id { + /// A unique identifier. + Unique(u64), + /// A hash identifier. + Hash(u64), +} + +impl Id { + fn unique() -> Self { + use std::sync::atomic::{self, AtomicU64}; + + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + + Self::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)) + } + + fn path(path: impl AsRef) -> Self { + let hash = { + let mut hasher = FxHasher::default(); + path.as_ref().hash(&mut hasher); + + hasher.finish() + }; + + Self::Hash(hash) + } +} + /// Image filtering strategy. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum FilterMethod { @@ -119,7 +145,7 @@ 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 measure_image(&self, handle: &Self::Handle) -> Size; -- cgit From 58ea914ad21ea9c5ae7b7b1c167ed084c9ddb07a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:52:49 +0200 Subject: Make `image::Id` actually opaque --- core/src/image.rs | 60 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'core/src') diff --git a/core/src/image.rs b/core/src/image.rs index a0e40787..c44ccc30 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -10,13 +10,26 @@ use std::path::{Path, PathBuf}; /// A handle of some image data. #[derive(Clone, PartialEq, Eq)] pub enum Handle { - /// File data + /// 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), - /// In-memory data + /// 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), - /// Decoded image pixels in RGBA format. + /// A handle pointing to decoded image pixels in RGBA format. + /// + /// Use [`from_rgba`] to create this variant. + /// + /// [`from_rgba`]: Self::from_bytes Rgba { /// The id of this handle. id: Id, @@ -39,12 +52,24 @@ impl Handle { Self::Path(Id::path(&path), path) } - /// Creates an image [`Handle`] containing the image pixels directly. This - /// function expects the input data to be provided as a `Vec` of RGBA - /// pixels. + /// 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_bytes(bytes: impl Into) -> Handle { + Self::Bytes(Id::unique(), bytes.into()) + } + + /// 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_pixels( + pub fn from_rgba( width: u32, height: u32, pixels: impl Into, @@ -57,16 +82,6 @@ impl Handle { } } - /// Creates an image [`Handle`] containing the 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 Into) -> Handle { - Self::Bytes(Id::unique(), bytes.into()) - } - /// Returns the unique identifier of the [`Handle`]. pub fn id(&self) -> Id { match self { @@ -100,10 +115,11 @@ impl std::fmt::Debug for Handle { /// The unique identifier of some [`Handle`] data. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Id { - /// A unique identifier. +pub struct Id(_Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum _Id { Unique(u64), - /// A hash identifier. Hash(u64), } @@ -113,7 +129,7 @@ impl Id { static NEXT_ID: AtomicU64 = AtomicU64::new(0); - Self::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)) + Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed))) } fn path(path: impl AsRef) -> Self { @@ -124,7 +140,7 @@ impl Id { hasher.finish() }; - Self::Hash(hash) + Self(_Id::Hash(hash)) } } -- cgit From 01b014c19fa2a3c200fb2077e31822f525f729cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:53:25 +0200 Subject: Fix documentation link in `image::Handle` --- core/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/src') diff --git a/core/src/image.rs b/core/src/image.rs index c44ccc30..c38239bc 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -29,7 +29,7 @@ pub enum Handle { /// /// Use [`from_rgba`] to create this variant. /// - /// [`from_rgba`]: Self::from_bytes + /// [`from_rgba`]: Self::from_rgba Rgba { /// The id of this handle. id: Id, -- cgit