Skip to main content

iced_core/
image.rs

1//! Load and draw raster graphics.
2use crate::border;
3use crate::{Bytes, Radians, Rectangle, Size};
4
5use rustc_hash::FxHasher;
6
7use std::hash::{Hash, Hasher};
8use std::io;
9use std::path::{Path, PathBuf};
10use std::sync::{Arc, Weak};
11
12/// A raster image that can be drawn.
13#[derive(Debug, Clone, PartialEq)]
14pub struct Image<H = Handle> {
15    /// The handle of the image.
16    pub handle: H,
17
18    /// The filter method of the image.
19    pub filter_method: FilterMethod,
20
21    /// The rotation to be applied to the image; on its center.
22    pub rotation: Radians,
23
24    /// The border radius of the [`Image`].
25    ///
26    /// Currently, this will only be applied to the `clip_bounds`.
27    pub border_radius: border::Radius,
28
29    /// The opacity of the image.
30    ///
31    /// 0 means transparent. 1 means opaque.
32    pub opacity: f32,
33}
34
35impl Image<Handle> {
36    /// Creates a new [`Image`] with the given handle.
37    pub fn new(handle: impl Into<Handle>) -> Self {
38        Self {
39            handle: handle.into(),
40            filter_method: FilterMethod::default(),
41            rotation: Radians(0.0),
42            border_radius: border::Radius::default(),
43            opacity: 1.0,
44        }
45    }
46
47    /// Sets the filter method of the [`Image`].
48    pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
49        self.filter_method = filter_method;
50        self
51    }
52
53    /// Sets the rotation of the [`Image`].
54    pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
55        self.rotation = rotation.into();
56        self
57    }
58
59    /// Sets the opacity of the [`Image`].
60    pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
61        self.opacity = opacity.into();
62        self
63    }
64}
65
66impl From<&Handle> for Image {
67    fn from(handle: &Handle) -> Self {
68        Image::new(handle.clone())
69    }
70}
71
72/// A handle of some image data.
73#[derive(Clone, PartialEq, Eq)]
74pub enum Handle {
75    /// A file handle. The image data will be read
76    /// from the file path.
77    ///
78    /// Use [`from_path`] to create this variant.
79    ///
80    /// [`from_path`]: Self::from_path
81    Path(Id, PathBuf),
82
83    /// A handle pointing to some encoded image bytes in-memory.
84    ///
85    /// Use [`from_bytes`] to create this variant.
86    ///
87    /// [`from_bytes`]: Self::from_bytes
88    Bytes(Id, Bytes),
89
90    /// A handle pointing to decoded image pixels in RGBA format.
91    ///
92    /// Use [`from_rgba`] to create this variant.
93    ///
94    /// [`from_rgba`]: Self::from_rgba
95    Rgba {
96        /// The id of this handle.
97        id: Id,
98        /// The width of the image.
99        width: u32,
100        /// The height of the image.
101        height: u32,
102        /// The pixels.
103        pixels: Bytes,
104    },
105}
106
107impl Handle {
108    /// Creates an image [`Handle`] pointing to the image of the given path.
109    ///
110    /// Makes an educated guess about the image format by examining the data in the file.
111    pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
112        let path = path.into();
113
114        Self::Path(Id::path(&path), path)
115    }
116
117    /// Creates an image [`Handle`] containing the encoded image data directly.
118    ///
119    /// Makes an educated guess about the image format by examining the given data.
120    ///
121    /// This is useful if you already have your image loaded in-memory, maybe
122    /// because you downloaded or generated it procedurally.
123    pub fn from_bytes(bytes: impl Into<Bytes>) -> Handle {
124        Self::Bytes(Id::unique(), bytes.into())
125    }
126
127    /// Creates an image [`Handle`] containing the decoded image pixels directly.
128    ///
129    /// This function expects the pixel data to be provided as a collection of [`Bytes`]
130    /// of RGBA pixels. Therefore, the length of the pixel data should always be
131    /// `width * height * 4`.
132    ///
133    /// This is useful if you have already decoded your image.
134    pub fn from_rgba(width: u32, height: u32, pixels: impl Into<Bytes>) -> Handle {
135        Self::Rgba {
136            id: Id::unique(),
137            width,
138            height,
139            pixels: pixels.into(),
140        }
141    }
142
143    /// Returns the unique identifier of the [`Handle`].
144    pub fn id(&self) -> Id {
145        match self {
146            Handle::Path(id, _) | Handle::Bytes(id, _) | Handle::Rgba { id, .. } => *id,
147        }
148    }
149}
150
151impl<T> From<T> for Handle
152where
153    T: Into<PathBuf>,
154{
155    fn from(path: T) -> Handle {
156        Handle::from_path(path.into())
157    }
158}
159
160impl From<&Handle> for Handle {
161    fn from(value: &Handle) -> Self {
162        value.clone()
163    }
164}
165
166impl std::fmt::Debug for Handle {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        match self {
169            Self::Path(id, path) => write!(f, "Path({id:?}, {path:?})"),
170            Self::Bytes(id, _) => write!(f, "Bytes({id:?}, ...)"),
171            Self::Rgba {
172                id, width, height, ..
173            } => {
174                write!(f, "Pixels({id:?}, {width} * {height})")
175            }
176        }
177    }
178}
179
180/// The unique identifier of some [`Handle`] data.
181#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
182pub struct Id(_Id);
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
185enum _Id {
186    Unique(u64),
187    Hash(u64),
188}
189
190impl Id {
191    fn unique() -> Self {
192        use std::sync::atomic::{self, AtomicU64};
193
194        static NEXT_ID: AtomicU64 = AtomicU64::new(0);
195
196        Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)))
197    }
198
199    fn path(path: impl AsRef<Path>) -> Self {
200        let hash = {
201            let mut hasher = FxHasher::default();
202            path.as_ref().hash(&mut hasher);
203
204            hasher.finish()
205        };
206
207        Self(_Id::Hash(hash))
208    }
209}
210
211/// Image filtering strategy.
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
213pub enum FilterMethod {
214    /// Bilinear interpolation.
215    #[default]
216    Linear,
217    /// Nearest neighbor.
218    Nearest,
219}
220
221/// A memory allocation of a [`Handle`], often in GPU memory.
222///
223/// Renderers tend to decode and upload image data concurrently to
224/// avoid blocking the user interface. This means that when you use a
225/// [`Handle`] in a widget, there may be a slight frame delay until it
226/// is finally visible. If you are animating images, this can cause
227/// undesirable flicker.
228///
229/// When you obtain an [`Allocation`] explicitly, you get the guarantee
230/// that using a [`Handle`] will draw the corresponding [`Image`]
231/// immediately in the next frame.
232///
233/// This guarantee is valid as long as you hold an [`Allocation`].
234/// Only when you drop all its clones, the renderer may choose to free
235/// the memory of the [`Handle`]. Be careful!
236#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct Allocation(Arc<Memory>);
238
239/// Some memory taken by an [`Allocation`].
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct Memory {
242    handle: Handle,
243    size: Size<u32>,
244}
245
246impl Allocation {
247    /// Returns a weak reference to the [`Memory`] of the [`Allocation`].
248    pub fn downgrade(&self) -> Weak<Memory> {
249        Arc::downgrade(&self.0)
250    }
251
252    /// Upgrades a [`Weak`] memory reference to an [`Allocation`].
253    pub fn upgrade(weak: &Weak<Memory>) -> Option<Allocation> {
254        Weak::upgrade(weak).map(Allocation)
255    }
256
257    /// Returns the [`Handle`] of this [`Allocation`].
258    pub fn handle(&self) -> &Handle {
259        &self.0.handle
260    }
261
262    /// Returns the [`Size`] of the image of this [`Allocation`].
263    pub fn size(&self) -> Size<u32> {
264        self.0.size
265    }
266}
267
268/// Creates a new [`Allocation`] for the given handle.
269///
270/// This should only be used internally by renderer implementations.
271///
272/// # Safety
273/// Must only be created once the [`Handle`] is allocated in memory.
274#[allow(unsafe_code)]
275pub unsafe fn allocate(handle: &Handle, size: Size<u32>) -> Allocation {
276    Allocation(Arc::new(Memory {
277        handle: handle.clone(),
278        size,
279    }))
280}
281
282/// A [`Renderer`] that can render raster graphics.
283///
284/// [renderer]: crate::renderer
285pub trait Renderer: crate::Renderer {
286    /// The image Handle to be displayed. Iced exposes its own default implementation of a [`Handle`]
287    ///
288    /// [`Handle`]: Self::Handle
289    type Handle: Clone;
290
291    /// Loads an image and returns an explicit [`Allocation`] to it.
292    ///
293    /// If the image is not already loaded, this method will block! You should
294    /// generally not use it in drawing logic if you want to avoid frame drops.
295    fn load_image(&self, handle: &Self::Handle) -> Result<Allocation, Error>;
296
297    /// Returns the dimensions of an image for the given [`Handle`].
298    ///
299    /// If the image is not already loaded, the [`Renderer`] may choose to return
300    /// `None`, load the image in the background, and then trigger a relayout.
301    ///
302    /// If you need a measurement right away, consider using [`Renderer::load_image`].
303    fn measure_image(&self, handle: &Self::Handle) -> Option<Size<u32>>;
304
305    /// Draws an [`Image`] inside the provided `bounds`.
306    ///
307    /// If the image is not already loaded, the [`Renderer`] may choose to render
308    /// nothing, load the image in the background, and then trigger a redraw.
309    ///
310    /// If you need to draw an image right away, consider using [`Renderer::load_image`]
311    /// and hold on to an [`Allocation`] first.
312    fn draw_image(&mut self, image: Image<Self::Handle>, bounds: Rectangle, clip_bounds: Rectangle);
313}
314
315/// An image loading error.
316#[derive(Debug, Clone, thiserror::Error)]
317pub enum Error {
318    /// The image data was invalid or could not be decoded.
319    #[error("the image data was invalid or could not be decoded: {0}")]
320    Invalid(Arc<dyn std::error::Error + Send + Sync>),
321    /// The image file was not found.
322    #[error("the image file could not be opened: {0}")]
323    Inaccessible(Arc<io::Error>),
324    /// Loading images is unsupported.
325    #[error("loading images is unsupported")]
326    Unsupported,
327    /// The image is empty.
328    #[error("the image is empty")]
329    Empty,
330    /// Not enough memory to allocate the image.
331    #[error("not enough memory to allocate the image")]
332    OutOfMemory,
333}