Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilize bytes::Bytes for images #2356

Merged
merged 7 commits into from
May 1, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use an opaque Id type for image::Handle
Hashing pointers is a terrible idea.
  • Loading branch information
hecrj committed Apr 30, 2024

Unverified

This user has not yet uploaded their public signing key.
commit b52c7bb610f593fffc624d461dca17ac50c81626
68 changes: 47 additions & 21 deletions core/src/image.rs
Original file line number Diff line number Diff line change
@@ -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<T: Into<PathBuf>>(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<Bytes>,
) -> 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<Bytes>) -> 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<H: Hasher>(&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,15 +89,45 @@ 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})")
}
}
}
}

/// 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<Path>) -> 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<u32>;
5 changes: 3 additions & 2 deletions graphics/src/image.rs
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ pub fn load(
}

let (width, height, pixels) = match handle {
image::Handle::Path(path) => {
image::Handle::Path(_, path) => {
let image = ::image::open(path)?;

let operation = std::fs::File::open(path)
@@ -119,7 +119,7 @@ pub fn load(
image::Bytes::from(rgba.into_raw()),
)
}
image::Handle::Bytes(bytes) => {
image::Handle::Bytes(_, bytes) => {
let image = ::image::load_from_memory(bytes)?;
let operation =
Operation::from_exif(&mut std::io::Cursor::new(bytes))
@@ -138,6 +138,7 @@ pub fn load(
width,
height,
pixels,
..
} => (*width, *height, pixels.clone()),
};

4 changes: 2 additions & 2 deletions tiny_skia/src/raster.rs
Original file line number Diff line number Diff line change
@@ -71,8 +71,8 @@ impl Pipeline {

#[derive(Debug, Default)]
struct Cache {
entries: FxHashMap<u64, Option<Entry>>,
hits: FxHashSet<u64>,
entries: FxHashMap<raster::Id, Option<Entry>>,
hits: FxHashSet<raster::Id>,
}

impl Cache {
4 changes: 2 additions & 2 deletions wgpu/src/image/raster.rs
Original file line number Diff line number Diff line change
@@ -38,8 +38,8 @@ impl Memory {
/// Caches image raster data
#[derive(Debug, Default)]
pub struct Cache {
map: FxHashMap<u64, Memory>,
hits: FxHashSet<u64>,
map: FxHashMap<image::Id, Memory>,
hits: FxHashSet<image::Id>,
should_trim: bool,
}

8 changes: 3 additions & 5 deletions widget/src/image.rs
Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@ use crate::core::{
ContentFit, Element, Layout, Length, Rectangle, Size, Vector, Widget,
};

use std::hash::Hash;

pub use image::{FilterMethod, Handle};

/// Creates a new [`Viewer`] with the given image `Handle`.
@@ -128,7 +126,7 @@ pub fn draw<Renderer, Handle>(
filter_method: FilterMethod,
) where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash,
Handle: Clone,
{
let Size { width, height } = renderer.measure_image(handle);
let image_size = Size::new(width as f32, height as f32);
@@ -167,7 +165,7 @@ impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
for Image<Handle>
where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash,
Handle: Clone,
{
fn size(&self) -> Size<Length> {
Size {
@@ -216,7 +214,7 @@ impl<'a, Message, Theme, Renderer, Handle> From<Image<Handle>>
for Element<'a, Message, Theme, Renderer>
where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash + 'a,
Handle: Clone + 'a,
{
fn from(image: Image<Handle>) -> Element<'a, Message, Theme, Renderer> {
Element::new(image)
6 changes: 2 additions & 4 deletions widget/src/image/viewer.rs
Original file line number Diff line number Diff line change
@@ -10,8 +10,6 @@ use crate::core::{
Vector, Widget,
};

use std::hash::Hash;

/// A frame that displays an image with the ability to zoom in/out and pan.
#[allow(missing_debug_implementations)]
pub struct Viewer<Handle> {
@@ -94,7 +92,7 @@ impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
for Viewer<Handle>
where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash,
Handle: Clone,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>()
@@ -401,7 +399,7 @@ impl<'a, Message, Theme, Renderer, Handle> From<Viewer<Handle>>
where
Renderer: 'a + image::Renderer<Handle = Handle>,
Message: 'a,
Handle: Clone + Hash + 'a,
Handle: Clone + 'a,
{
fn from(viewer: Viewer<Handle>) -> Element<'a, Message, Theme, Renderer> {
Element::new(viewer)
Loading