1#[derive(Clone, Debug, PartialEq)]
2pub struct Image {
3 pub width: u32,
4 pub height: u32,
5 pub pixels: Vec<[f32; 4]>,
6}
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct ImageError;
10
11impl std::fmt::Display for ImageError {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 f.write_str("pixel count does not match the image size")
14 }
15}
16
17impl std::error::Error for ImageError {}
18
19impl Image {
20 pub fn new(width: u32, height: u32) -> Self {
21 Self::solid(width, height, [0.0, 0.0, 0.0, 1.0])
22 }
23
24 pub fn solid(width: u32, height: u32, pixel: [f32; 4]) -> Self {
25 let n = (width as usize).saturating_mul(height as usize);
26 Self {
27 width,
28 height,
29 pixels: vec![pixel; n],
30 }
31 }
32
33 pub fn from_pixels(width: u32, height: u32, pixels: Vec<[f32; 4]>) -> Result<Self, ImageError> {
34 if pixels.len() != (width as usize).saturating_mul(height as usize) {
35 return Err(ImageError);
36 }
37 Ok(Self {
38 width,
39 height,
40 pixels,
41 })
42 }
43
44 pub fn from_fn(width: u32, height: u32, mut f: impl FnMut(u32, u32) -> [f32; 4]) -> Self {
45 let mut pixels = Vec::with_capacity((width as usize).saturating_mul(height as usize));
46 for y in 0..height {
47 for x in 0..width {
48 pixels.push(f(x, y));
49 }
50 }
51 Self {
52 width,
53 height,
54 pixels,
55 }
56 }
57
58 pub fn index(&self, x: u32, y: u32) -> usize {
59 (y * self.width + x) as usize
60 }
61
62 pub fn at(&self, x: u32, y: u32) -> [f32; 4] {
63 self.pixels[self.index(x, y)]
64 }
65
66 pub fn get(&self, x: i32, y: i32) -> [f32; 4] {
67 if self.width == 0 || self.height == 0 {
68 return [0.0; 4];
69 }
70 let x = x.clamp(0, self.width as i32 - 1) as u32;
71 let y = y.clamp(0, self.height as i32 - 1) as u32;
72 self.at(x, y)
73 }
74
75 pub fn sample(&self, x: f32, y: f32) -> [f32; 4] {
76 if self.width == 0 || self.height == 0 {
77 return [0.0; 4];
78 }
79 let max_x = self.width as f32 - 1.0;
80 let max_y = self.height as f32 - 1.0;
81 let x = x.clamp(0.0, max_x);
82 let y = y.clamp(0.0, max_y);
83 let x0 = x.floor() as i32;
84 let y0 = y.floor() as i32;
85 let tx = x - x0 as f32;
86 let ty = y - y0 as f32;
87 let a = self.get(x0, y0);
88 let b = self.get(x0 + 1, y0);
89 let c = self.get(x0, y0 + 1);
90 let d = self.get(x0 + 1, y0 + 1);
91 let mut out = [0.0; 4];
92 for i in 0..4 {
93 let ab = a[i] + (b[i] - a[i]) * tx;
94 let cd = c[i] + (d[i] - c[i]) * tx;
95 out[i] = ab + (cd - ab) * ty;
96 }
97 out
98 }
99
100 pub fn map_rgb(&self, mut f: impl FnMut(u32, u32, [f32; 3]) -> [f32; 3]) -> Self {
101 let mut out = self.clone();
102 for y in 0..self.height {
103 for x in 0..self.width {
104 let i = self.index(x, y);
105 let p = self.pixels[i];
106 let c = f(x, y, [p[0], p[1], p[2]]);
107 out.pixels[i] = [c[0], c[1], c[2], p[3]];
108 }
109 }
110 out
111 }
112}
113
114pub fn rgb(p: [f32; 4]) -> [f32; 3] {
115 [p[0], p[1], p[2]]
116}
117
118pub fn px(c: [f32; 3], a: f32) -> [f32; 4] {
119 [c[0], c[1], c[2], a]
120}