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}