epaint/
image.rs

1use crate::{textures::TextureOptions, Color32};
2use std::sync::Arc;
3
4/// An image stored in RAM.
5///
6/// To load an image file, see [`ColorImage::from_rgba_unmultiplied`].
7///
8/// In order to paint the image on screen, you first need to convert it to
9///
10/// See also: [`ColorImage`], [`FontImage`].
11#[derive(Clone, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
13pub enum ImageData {
14    /// RGBA image.
15    Color(Arc<ColorImage>),
16
17    /// Used for the font texture.
18    Font(FontImage),
19}
20
21impl ImageData {
22    pub fn size(&self) -> [usize; 2] {
23        match self {
24            Self::Color(image) => image.size,
25            Self::Font(image) => image.size,
26        }
27    }
28
29    pub fn width(&self) -> usize {
30        self.size()[0]
31    }
32
33    pub fn height(&self) -> usize {
34        self.size()[1]
35    }
36
37    pub fn bytes_per_pixel(&self) -> usize {
38        match self {
39            Self::Color(_) | Self::Font(_) => 4,
40        }
41    }
42}
43
44// ----------------------------------------------------------------------------
45
46/// A 2D RGBA color image in RAM.
47#[derive(Clone, Default, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
49pub struct ColorImage {
50    /// width, height.
51    pub size: [usize; 2],
52
53    /// The pixels, row by row, from top to bottom.
54    pub pixels: Vec<Color32>,
55}
56
57impl ColorImage {
58    /// Create an image filled with the given color.
59    pub fn new(size: [usize; 2], color: Color32) -> Self {
60        Self {
61            size,
62            pixels: vec![color; size[0] * size[1]],
63        }
64    }
65
66    /// Create a [`ColorImage`] from flat un-multiplied RGBA data.
67    ///
68    /// This is usually what you want to use after having loaded an image file.
69    ///
70    /// Panics if `size[0] * size[1] * 4 != rgba.len()`.
71    ///
72    /// ## Example using the [`image`](crates.io/crates/image) crate:
73    /// ``` ignore
74    /// fn load_image_from_path(path: &std::path::Path) -> Result<egui::ColorImage, image::ImageError> {
75    ///     let image = image::io::Reader::open(path)?.decode()?;
76    ///     let size = [image.width() as _, image.height() as _];
77    ///     let image_buffer = image.to_rgba8();
78    ///     let pixels = image_buffer.as_flat_samples();
79    ///     Ok(egui::ColorImage::from_rgba_unmultiplied(
80    ///         size,
81    ///         pixels.as_slice(),
82    ///     ))
83    /// }
84    ///
85    /// fn load_image_from_memory(image_data: &[u8]) -> Result<ColorImage, image::ImageError> {
86    ///     let image = image::load_from_memory(image_data)?;
87    ///     let size = [image.width() as _, image.height() as _];
88    ///     let image_buffer = image.to_rgba8();
89    ///     let pixels = image_buffer.as_flat_samples();
90    ///     Ok(ColorImage::from_rgba_unmultiplied(
91    ///         size,
92    ///         pixels.as_slice(),
93    ///     ))
94    /// }
95    /// ```
96    pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
97        assert_eq!(size[0] * size[1] * 4, rgba.len());
98        let pixels = rgba
99            .chunks_exact(4)
100            .map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
101            .collect();
102        Self { size, pixels }
103    }
104
105    pub fn from_rgba_premultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
106        assert_eq!(size[0] * size[1] * 4, rgba.len());
107        let pixels = rgba
108            .chunks_exact(4)
109            .map(|p| Color32::from_rgba_premultiplied(p[0], p[1], p[2], p[3]))
110            .collect();
111        Self { size, pixels }
112    }
113
114    /// Create a [`ColorImage`] from flat opaque gray data.
115    ///
116    /// Panics if `size[0] * size[1] != gray.len()`.
117    pub fn from_gray(size: [usize; 2], gray: &[u8]) -> Self {
118        assert_eq!(size[0] * size[1], gray.len());
119        let pixels = gray.iter().map(|p| Color32::from_gray(*p)).collect();
120        Self { size, pixels }
121    }
122
123    /// Alternative method to `from_gray`.
124    /// Create a [`ColorImage`] from iterator over flat opaque gray data.
125    ///
126    /// Panics if `size[0] * size[1] != gray_iter.len()`.
127    pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator<Item = u8>) -> Self {
128        let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect();
129        assert_eq!(size[0] * size[1], pixels.len());
130        Self { size, pixels }
131    }
132
133    /// A view of the underlying data as `&[u8]`
134    #[cfg(feature = "bytemuck")]
135    pub fn as_raw(&self) -> &[u8] {
136        bytemuck::cast_slice(&self.pixels)
137    }
138
139    /// A view of the underlying data as `&mut [u8]`
140    #[cfg(feature = "bytemuck")]
141    pub fn as_raw_mut(&mut self) -> &mut [u8] {
142        bytemuck::cast_slice_mut(&mut self.pixels)
143    }
144
145    /// Create a new Image from a patch of the current image. This method is especially convenient for screenshotting a part of the app
146    /// since `region` can be interpreted as screen coordinates of the entire screenshot if `pixels_per_point` is provided for the native application.
147    /// The floats of [`emath::Rect`] are cast to usize, rounding them down in order to interpret them as indices to the image data.
148    ///
149    /// Panics if `region.min.x > region.max.x || region.min.y > region.max.y`, or if a region larger than the image is passed.
150    pub fn region(&self, region: &emath::Rect, pixels_per_point: Option<f32>) -> Self {
151        let pixels_per_point = pixels_per_point.unwrap_or(1.0);
152        let min_x = (region.min.x * pixels_per_point) as usize;
153        let max_x = (region.max.x * pixels_per_point) as usize;
154        let min_y = (region.min.y * pixels_per_point) as usize;
155        let max_y = (region.max.y * pixels_per_point) as usize;
156        assert!(min_x <= max_x);
157        assert!(min_y <= max_y);
158        let width = max_x - min_x;
159        let height = max_y - min_y;
160        let mut output = Vec::with_capacity(width * height);
161        let row_stride = self.size[0];
162
163        for row in min_y..max_y {
164            output.extend_from_slice(
165                &self.pixels[row * row_stride + min_x..row * row_stride + max_x],
166            );
167        }
168        Self {
169            size: [width, height],
170            pixels: output,
171        }
172    }
173
174    /// Create a [`ColorImage`] from flat RGB data.
175    ///
176    /// This is what you want to use after having loaded an image file (and if
177    /// you are ignoring the alpha channel - considering it to always be 0xff)
178    ///
179    /// Panics if `size[0] * size[1] * 3 != rgb.len()`.
180    pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self {
181        assert_eq!(size[0] * size[1] * 3, rgb.len());
182        let pixels = rgb
183            .chunks_exact(3)
184            .map(|p| Color32::from_rgb(p[0], p[1], p[2]))
185            .collect();
186        Self { size, pixels }
187    }
188
189    /// An example color image, useful for tests.
190    pub fn example() -> Self {
191        let width = 128;
192        let height = 64;
193        let mut img = Self::new([width, height], Color32::TRANSPARENT);
194        for y in 0..height {
195            for x in 0..width {
196                let h = x as f32 / width as f32;
197                let s = 1.0;
198                let v = 1.0;
199                let a = y as f32 / height as f32;
200                img[(x, y)] = crate::Hsva { h, s, v, a }.into();
201            }
202        }
203        img
204    }
205
206    #[inline]
207    pub fn width(&self) -> usize {
208        self.size[0]
209    }
210
211    #[inline]
212    pub fn height(&self) -> usize {
213        self.size[1]
214    }
215}
216
217impl std::ops::Index<(usize, usize)> for ColorImage {
218    type Output = Color32;
219
220    #[inline]
221    fn index(&self, (x, y): (usize, usize)) -> &Color32 {
222        let [w, h] = self.size;
223        assert!(x < w && y < h);
224        &self.pixels[y * w + x]
225    }
226}
227
228impl std::ops::IndexMut<(usize, usize)> for ColorImage {
229    #[inline]
230    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 {
231        let [w, h] = self.size;
232        assert!(x < w && y < h);
233        &mut self.pixels[y * w + x]
234    }
235}
236
237impl From<ColorImage> for ImageData {
238    #[inline(always)]
239    fn from(image: ColorImage) -> Self {
240        Self::Color(Arc::new(image))
241    }
242}
243
244impl From<Arc<ColorImage>> for ImageData {
245    #[inline]
246    fn from(image: Arc<ColorImage>) -> Self {
247        Self::Color(image)
248    }
249}
250
251impl std::fmt::Debug for ColorImage {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        f.debug_struct("ColorImage")
254            .field("size", &self.size)
255            .field("pixel-count", &self.pixels.len())
256            .finish_non_exhaustive()
257    }
258}
259
260// ----------------------------------------------------------------------------
261
262/// A single-channel image designed for the font texture.
263///
264/// Each value represents "coverage", i.e. how much a texel is covered by a character.
265///
266/// This is roughly interpreted as the opacity of a white image.
267#[derive(Clone, Default, PartialEq)]
268#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
269pub struct FontImage {
270    /// width, height
271    pub size: [usize; 2],
272
273    /// The coverage value.
274    ///
275    /// Often you want to use [`Self::srgba_pixels`] instead.
276    pub pixels: Vec<f32>,
277}
278
279impl FontImage {
280    pub fn new(size: [usize; 2]) -> Self {
281        Self {
282            size,
283            pixels: vec![0.0; size[0] * size[1]],
284        }
285    }
286
287    #[inline]
288    pub fn width(&self) -> usize {
289        self.size[0]
290    }
291
292    #[inline]
293    pub fn height(&self) -> usize {
294        self.size[1]
295    }
296
297    /// Returns the textures as `sRGBA` premultiplied pixels, row by row, top to bottom.
298    ///
299    /// `gamma` should normally be set to `None`.
300    ///
301    /// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`.
302    #[inline]
303    pub fn srgba_pixels(&self, gamma: Option<f32>) -> impl ExactSizeIterator<Item = Color32> + '_ {
304        let gamma = gamma.unwrap_or(0.55); // TODO(emilk): this default coverage gamma is a magic constant, chosen by eye. I don't even know why we need it.
305        self.pixels.iter().map(move |coverage| {
306            let alpha = coverage.powf(gamma);
307            // We want to multiply with `vec4(alpha)` in the fragment shader:
308            let a = fast_round(alpha * 255.0);
309            Color32::from_rgba_premultiplied(a, a, a, a)
310        })
311    }
312
313    /// Clone a sub-region as a new image.
314    pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self {
315        assert!(x + w <= self.width());
316        assert!(y + h <= self.height());
317
318        let mut pixels = Vec::with_capacity(w * h);
319        for y in y..y + h {
320            let offset = y * self.width() + x;
321            pixels.extend(&self.pixels[offset..(offset + w)]);
322        }
323        assert_eq!(pixels.len(), w * h);
324        Self {
325            size: [w, h],
326            pixels,
327        }
328    }
329}
330
331impl std::ops::Index<(usize, usize)> for FontImage {
332    type Output = f32;
333
334    #[inline]
335    fn index(&self, (x, y): (usize, usize)) -> &f32 {
336        let [w, h] = self.size;
337        assert!(x < w && y < h);
338        &self.pixels[y * w + x]
339    }
340}
341
342impl std::ops::IndexMut<(usize, usize)> for FontImage {
343    #[inline]
344    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 {
345        let [w, h] = self.size;
346        assert!(x < w && y < h);
347        &mut self.pixels[y * w + x]
348    }
349}
350
351impl From<FontImage> for ImageData {
352    #[inline(always)]
353    fn from(image: FontImage) -> Self {
354        Self::Font(image)
355    }
356}
357
358#[inline]
359fn fast_round(r: f32) -> u8 {
360    (r + 0.5) as _ // rust does a saturating cast since 1.45
361}
362
363// ----------------------------------------------------------------------------
364
365/// A change to an image.
366///
367/// Either a whole new image, or an update to a rectangular region of it.
368#[derive(Clone, PartialEq)]
369#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
370#[must_use = "The painter must take care of this"]
371pub struct ImageDelta {
372    /// What to set the texture to.
373    ///
374    /// If [`Self::pos`] is `None`, this describes the whole texture.
375    ///
376    /// If [`Self::pos`] is `Some`, this describes a patch of the whole image starting at [`Self::pos`].
377    pub image: ImageData,
378
379    pub options: TextureOptions,
380
381    /// If `None`, set the whole texture to [`Self::image`].
382    ///
383    /// If `Some(pos)`, update a sub-region of an already allocated texture with the patch in [`Self::image`].
384    pub pos: Option<[usize; 2]>,
385}
386
387impl ImageDelta {
388    /// Update the whole texture.
389    pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {
390        Self {
391            image: image.into(),
392            options,
393            pos: None,
394        }
395    }
396
397    /// Update a sub-region of an existing texture.
398    pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {
399        Self {
400            image: image.into(),
401            options,
402            pos: Some(pos),
403        }
404    }
405
406    /// Is this affecting the whole texture?
407    /// If `false`, this is a partial (sub-region) update.
408    pub fn is_whole(&self) -> bool {
409        self.pos.is_none()
410    }
411}