1use crate::{textures::TextureOptions, Color32};
2use std::sync::Arc;
3
4#[derive(Clone, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
13pub enum ImageData {
14 Color(Arc<ColorImage>),
16
17 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#[derive(Clone, Default, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
49pub struct ColorImage {
50 pub size: [usize; 2],
52
53 pub pixels: Vec<Color32>,
55}
56
57impl ColorImage {
58 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 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 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 #[doc(alias = "from_grey_iter")]
128 pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator<Item = u8>) -> Self {
129 let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect();
130 assert_eq!(size[0] * size[1], pixels.len());
131 Self { size, pixels }
132 }
133
134 #[cfg(feature = "bytemuck")]
136 pub fn as_raw(&self) -> &[u8] {
137 bytemuck::cast_slice(&self.pixels)
138 }
139
140 #[cfg(feature = "bytemuck")]
142 pub fn as_raw_mut(&mut self) -> &mut [u8] {
143 bytemuck::cast_slice_mut(&mut self.pixels)
144 }
145
146 pub fn region(&self, region: &emath::Rect, pixels_per_point: Option<f32>) -> Self {
152 let pixels_per_point = pixels_per_point.unwrap_or(1.0);
153 let min_x = (region.min.x * pixels_per_point) as usize;
154 let max_x = (region.max.x * pixels_per_point) as usize;
155 let min_y = (region.min.y * pixels_per_point) as usize;
156 let max_y = (region.max.y * pixels_per_point) as usize;
157 assert!(
158 min_x <= max_x && min_y <= max_y,
159 "Screenshot region is invalid: {region:?}"
160 );
161 let width = max_x - min_x;
162 let height = max_y - min_y;
163 let mut output = Vec::with_capacity(width * height);
164 let row_stride = self.size[0];
165
166 for row in min_y..max_y {
167 output.extend_from_slice(
168 &self.pixels[row * row_stride + min_x..row * row_stride + max_x],
169 );
170 }
171 Self {
172 size: [width, height],
173 pixels: output,
174 }
175 }
176
177 pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self {
184 assert_eq!(size[0] * size[1] * 3, rgb.len());
185 let pixels = rgb
186 .chunks_exact(3)
187 .map(|p| Color32::from_rgb(p[0], p[1], p[2]))
188 .collect();
189 Self { size, pixels }
190 }
191
192 pub fn example() -> Self {
194 let width = 128;
195 let height = 64;
196 let mut img = Self::new([width, height], Color32::TRANSPARENT);
197 for y in 0..height {
198 for x in 0..width {
199 let h = x as f32 / width as f32;
200 let s = 1.0;
201 let v = 1.0;
202 let a = y as f32 / height as f32;
203 img[(x, y)] = crate::Hsva { h, s, v, a }.into();
204 }
205 }
206 img
207 }
208
209 #[inline]
210 pub fn width(&self) -> usize {
211 self.size[0]
212 }
213
214 #[inline]
215 pub fn height(&self) -> usize {
216 self.size[1]
217 }
218}
219
220impl std::ops::Index<(usize, usize)> for ColorImage {
221 type Output = Color32;
222
223 #[inline]
224 fn index(&self, (x, y): (usize, usize)) -> &Color32 {
225 let [w, h] = self.size;
226 assert!(x < w && y < h);
227 &self.pixels[y * w + x]
228 }
229}
230
231impl std::ops::IndexMut<(usize, usize)> for ColorImage {
232 #[inline]
233 fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 {
234 let [w, h] = self.size;
235 assert!(x < w && y < h);
236 &mut self.pixels[y * w + x]
237 }
238}
239
240impl From<ColorImage> for ImageData {
241 #[inline(always)]
242 fn from(image: ColorImage) -> Self {
243 Self::Color(Arc::new(image))
244 }
245}
246
247impl From<Arc<ColorImage>> for ImageData {
248 #[inline]
249 fn from(image: Arc<ColorImage>) -> Self {
250 Self::Color(image)
251 }
252}
253
254impl std::fmt::Debug for ColorImage {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 f.debug_struct("ColorImage")
257 .field("size", &self.size)
258 .field("pixel-count", &self.pixels.len())
259 .finish_non_exhaustive()
260 }
261}
262
263#[derive(Clone, Default, PartialEq)]
271#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
272pub struct FontImage {
273 pub size: [usize; 2],
275
276 pub pixels: Vec<f32>,
280}
281
282impl FontImage {
283 pub fn new(size: [usize; 2]) -> Self {
284 Self {
285 size,
286 pixels: vec![0.0; size[0] * size[1]],
287 }
288 }
289
290 #[inline]
291 pub fn width(&self) -> usize {
292 self.size[0]
293 }
294
295 #[inline]
296 pub fn height(&self) -> usize {
297 self.size[1]
298 }
299
300 #[inline]
306 pub fn srgba_pixels(&self, gamma: Option<f32>) -> impl ExactSizeIterator<Item = Color32> + '_ {
307 let gamma = gamma.unwrap_or(0.55);
310 self.pixels.iter().map(move |coverage| {
311 let alpha = coverage.powf(gamma);
312 let a = fast_round(alpha * 255.0);
314 Color32::from_rgba_premultiplied(a, a, a, a)
315 })
316 }
317
318 pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self {
320 assert!(x + w <= self.width());
321 assert!(y + h <= self.height());
322
323 let mut pixels = Vec::with_capacity(w * h);
324 for y in y..y + h {
325 let offset = y * self.width() + x;
326 pixels.extend(&self.pixels[offset..(offset + w)]);
327 }
328 assert_eq!(pixels.len(), w * h);
329 Self {
330 size: [w, h],
331 pixels,
332 }
333 }
334}
335
336impl std::ops::Index<(usize, usize)> for FontImage {
337 type Output = f32;
338
339 #[inline]
340 fn index(&self, (x, y): (usize, usize)) -> &f32 {
341 let [w, h] = self.size;
342 assert!(x < w && y < h);
343 &self.pixels[y * w + x]
344 }
345}
346
347impl std::ops::IndexMut<(usize, usize)> for FontImage {
348 #[inline]
349 fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 {
350 let [w, h] = self.size;
351 assert!(x < w && y < h);
352 &mut self.pixels[y * w + x]
353 }
354}
355
356impl From<FontImage> for ImageData {
357 #[inline(always)]
358 fn from(image: FontImage) -> Self {
359 Self::Font(image)
360 }
361}
362
363#[inline]
364fn fast_round(r: f32) -> u8 {
365 (r + 0.5) as _ }
367
368#[derive(Clone, PartialEq)]
374#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
375#[must_use = "The painter must take care of this"]
376pub struct ImageDelta {
377 pub image: ImageData,
383
384 pub options: TextureOptions,
385
386 pub pos: Option<[usize; 2]>,
390}
391
392impl ImageDelta {
393 pub fn full(image: impl Into<ImageData>, options: TextureOptions) -> Self {
395 Self {
396 image: image.into(),
397 options,
398 pos: None,
399 }
400 }
401
402 pub fn partial(pos: [usize; 2], image: impl Into<ImageData>, options: TextureOptions) -> Self {
404 Self {
405 image: image.into(),
406 options,
407 pos: Some(pos),
408 }
409 }
410
411 pub fn is_whole(&self) -> bool {
414 self.pos.is_none()
415 }
416}