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