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