iced_core/
image.rs

1//! Load and draw raster graphics.
2pub use bytes::Bytes;
3
4use crate::{Radians, Rectangle, Size};
5
6use rustc_hash::FxHasher;
7use std::hash::{Hash, Hasher};
8use std::path::{Path, PathBuf};
9
10/// A raster image that can be drawn.
11#[derive(Debug, Clone, PartialEq)]
12pub struct Image<H = Handle> {
13    /// The handle of the image.
14    pub handle: H,
15
16    /// The filter method of the image.
17    pub filter_method: FilterMethod,
18
19    /// The rotation to be applied to the image; on its center.
20    pub rotation: Radians,
21
22    /// The opacity of the image.
23    ///
24    /// 0 means transparent. 1 means opaque.
25    pub opacity: f32,
26
27    /// If set to `true`, the image will be snapped to the pixel grid.
28    ///
29    /// This can avoid graphical glitches, specially when using
30    /// [`FilterMethod::Nearest`].
31    pub snap: bool,
32}
33
34impl Image<Handle> {
35    /// Creates a new [`Image`] with the given handle.
36    pub fn new(handle: impl Into<Handle>) -> Self {
37        Self {
38            handle: handle.into(),
39            filter_method: FilterMethod::default(),
40            rotation: Radians(0.0),
41            opacity: 1.0,
42            snap: false,
43        }
44    }
45
46    /// Sets the filter method of the [`Image`].
47    pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
48        self.filter_method = filter_method;
49        self
50    }
51
52    /// Sets the rotation of the [`Image`].
53    pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
54        self.rotation = rotation.into();
55        self
56    }
57
58    /// Sets the opacity of the [`Image`].
59    pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
60        self.opacity = opacity.into();
61        self
62    }
63
64    /// Sets whether the [`Image`] should be snapped to the pixel grid.
65    pub fn snap(mut self, snap: bool) -> Self {
66        self.snap = snap;
67        self
68    }
69}
70
71impl From<&Handle> for Image {
72    fn from(handle: &Handle) -> Self {
73        Image::new(handle.clone())
74    }
75}
76
77/// A handle of some image data.
78#[derive(Clone, PartialEq, Eq)]
79pub enum Handle {
80    /// A file handle. The image data will be read
81    /// from the file path.
82    ///
83    /// Use [`from_path`] to create this variant.
84    ///
85    /// [`from_path`]: Self::from_path
86    Path(Id, PathBuf),
87
88    /// A handle pointing to some encoded image bytes in-memory.
89    ///
90    /// Use [`from_bytes`] to create this variant.
91    ///
92    /// [`from_bytes`]: Self::from_bytes
93    Bytes(Id, Bytes),
94
95    /// A handle pointing to decoded image pixels in RGBA format.
96    ///
97    /// Use [`from_rgba`] to create this variant.
98    ///
99    /// [`from_rgba`]: Self::from_rgba
100    Rgba {
101        /// The id of this handle.
102        id: Id,
103        /// The width of the image.
104        width: u32,
105        /// The height of the image.
106        height: u32,
107        /// The pixels.
108        pixels: Bytes,
109    },
110}
111
112impl Handle {
113    /// Creates an image [`Handle`] pointing to the image of the given path.
114    ///
115    /// Makes an educated guess about the image format by examining the data in the file.
116    pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
117        let path = path.into();
118
119        Self::Path(Id::path(&path), path)
120    }
121
122    /// Creates an image [`Handle`] containing the encoded image data directly.
123    ///
124    /// Makes an educated guess about the image format by examining the given data.
125    ///
126    /// This is useful if you already have your image loaded in-memory, maybe
127    /// because you downloaded or generated it procedurally.
128    pub fn from_bytes(bytes: impl Into<Bytes>) -> Handle {
129        Self::Bytes(Id::unique(), bytes.into())
130    }
131
132    /// Creates an image [`Handle`] containing the decoded image pixels directly.
133    ///
134    /// This function expects the pixel data to be provided as a collection of [`Bytes`]
135    /// of RGBA pixels. Therefore, the length of the pixel data should always be
136    /// `width * height * 4`.
137    ///
138    /// This is useful if you have already decoded your image.
139    pub fn from_rgba(
140        width: u32,
141        height: u32,
142        pixels: impl Into<Bytes>,
143    ) -> Handle {
144        Self::Rgba {
145            id: Id::unique(),
146            width,
147            height,
148            pixels: pixels.into(),
149        }
150    }
151
152    /// Returns the unique identifier of the [`Handle`].
153    pub fn id(&self) -> Id {
154        match self {
155            Handle::Path(id, _)
156            | Handle::Bytes(id, _)
157            | Handle::Rgba { id, .. } => *id,
158        }
159    }
160}
161
162impl<T> From<T> for Handle
163where
164    T: Into<PathBuf>,
165{
166    fn from(path: T) -> Handle {
167        Handle::from_path(path.into())
168    }
169}
170
171impl From<&Handle> for Handle {
172    fn from(value: &Handle) -> Self {
173        value.clone()
174    }
175}
176
177impl std::fmt::Debug for Handle {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        match self {
180            Self::Path(_, path) => write!(f, "Path({path:?})"),
181            Self::Bytes(_, _) => write!(f, "Bytes(...)"),
182            Self::Rgba { width, height, .. } => {
183                write!(f, "Pixels({width} * {height})")
184            }
185        }
186    }
187}
188
189/// The unique identifier of some [`Handle`] data.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
191pub struct Id(_Id);
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194enum _Id {
195    Unique(u64),
196    Hash(u64),
197}
198
199impl Id {
200    fn unique() -> Self {
201        use std::sync::atomic::{self, AtomicU64};
202
203        static NEXT_ID: AtomicU64 = AtomicU64::new(0);
204
205        Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)))
206    }
207
208    fn path(path: impl AsRef<Path>) -> Self {
209        let hash = {
210            let mut hasher = FxHasher::default();
211            path.as_ref().hash(&mut hasher);
212
213            hasher.finish()
214        };
215
216        Self(_Id::Hash(hash))
217    }
218}
219
220/// Image filtering strategy.
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
222pub enum FilterMethod {
223    /// Bilinear interpolation.
224    #[default]
225    Linear,
226    /// Nearest neighbor.
227    Nearest,
228}
229
230/// A [`Renderer`] that can render raster graphics.
231///
232/// [renderer]: crate::renderer
233pub trait Renderer: crate::Renderer {
234    /// The image Handle to be displayed. Iced exposes its own default implementation of a [`Handle`]
235    ///
236    /// [`Handle`]: Self::Handle
237    type Handle: Clone;
238
239    /// Returns the dimensions of an image for the given [`Handle`].
240    fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;
241
242    /// Draws an [`Image`] inside the provided `bounds`.
243    fn draw_image(&mut self, image: Image<Self::Handle>, bounds: Rectangle);
244}