epaint/
image.rs

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