iced_core/
image.rs

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