1use wide::u32x4;
2use xxhash_rust::xxh3::xxh3_64_with_seed;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
5pub enum ImageFilter {
6 #[default]
7 Nearest,
8 Linear,
9}
10
11#[derive(Debug, Clone)]
12pub struct ImageData {
13 pub id: u64,
17 pub pixels: Vec<u8>,
19 pub width: u32,
20 pub height: u32,
21}
22
23impl ImageData {
24 pub fn new(pixels: Vec<u8>, width: u32, height: u32) -> Self {
25 assert_eq!(
26 pixels.len(),
27 (width * height * 4) as usize,
28 "pixels must be RGBA8: width * height * 4 bytes"
29 );
30 let mut pixels = pixels;
31 premultiply_rgba(&mut pixels);
32 Self::addressed(pixels, width, height)
33 }
34
35 pub fn from_premultiplied(pixels: Vec<u8>, width: u32, height: u32) -> Self {
37 assert_eq!(
38 pixels.len(),
39 (width * height * 4) as usize,
40 "pixels must be RGBA8: width * height * 4 bytes"
41 );
42 Self::addressed(pixels, width, height)
43 }
44
45 fn addressed(pixels: Vec<u8>, width: u32, height: u32) -> Self {
47 let seed = ((width as u64) << 32) | height as u64;
48 Self {
49 id: xxh3_64_with_seed(&pixels, seed),
50 pixels,
51 width,
52 height,
53 }
54 }
55}
56
57#[inline]
58pub fn premultiply_rgba(pixels: &mut [u8]) {
59 let mut iter = pixels.chunks_exact_mut(16);
60 for chunk in iter.by_ref() {
61 let r = u32x4::new([
62 chunk[0] as u32,
63 chunk[4] as u32,
64 chunk[8] as u32,
65 chunk[12] as u32,
66 ]);
67 let g = u32x4::new([
68 chunk[1] as u32,
69 chunk[5] as u32,
70 chunk[9] as u32,
71 chunk[13] as u32,
72 ]);
73 let b = u32x4::new([
74 chunk[2] as u32,
75 chunk[6] as u32,
76 chunk[10] as u32,
77 chunk[14] as u32,
78 ]);
79 let a = u32x4::new([
80 chunk[3] as u32,
81 chunk[7] as u32,
82 chunk[11] as u32,
83 chunk[15] as u32,
84 ]);
85 let bias = u32x4::splat(128);
86 let shift = u32x4::splat(8);
87 let r_new = ((r * a) + bias) >> shift;
88 let g_new = ((g * a) + bias) >> shift;
89 let b_new = ((b * a) + bias) >> shift;
90 let ra = r_new.to_array();
91 let ga = g_new.to_array();
92 let ba = b_new.to_array();
93 chunk[0] = ra[0] as u8;
94 chunk[4] = ra[1] as u8;
95 chunk[8] = ra[2] as u8;
96 chunk[12] = ra[3] as u8;
97 chunk[1] = ga[0] as u8;
98 chunk[5] = ga[1] as u8;
99 chunk[9] = ga[2] as u8;
100 chunk[13] = ga[3] as u8;
101 chunk[2] = ba[0] as u8;
102 chunk[6] = ba[1] as u8;
103 chunk[10] = ba[2] as u8;
104 chunk[14] = ba[3] as u8;
105 }
106 for chunk in iter.into_remainder().chunks_exact_mut(4) {
107 let a = chunk[3] as u32;
108 chunk[0] = ((chunk[0] as u32 * a + 128) >> 8) as u8;
109 chunk[1] = ((chunk[1] as u32 * a + 128) >> 8) as u8;
110 chunk[2] = ((chunk[2] as u32 * a + 128) >> 8) as u8;
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 fn opaque(pixels: &[[u8; 3]]) -> Vec<u8> {
119 pixels
120 .iter()
121 .flat_map(|[r, g, b]| [*r, *g, *b, 255])
122 .collect()
123 }
124
125 #[test]
127 fn the_same_image_built_twice_gets_the_same_id() {
128 let once = ImageData::new(opaque(&[[10, 20, 30], [40, 50, 60]]), 2, 1);
129 let again = ImageData::new(opaque(&[[10, 20, 30], [40, 50, 60]]), 2, 1);
130 assert_eq!(once.id, again.id);
131 }
132
133 #[test]
134 fn different_pixels_get_different_ids() {
135 let a = ImageData::new(opaque(&[[10, 20, 30], [40, 50, 60]]), 2, 1);
136 let b = ImageData::new(opaque(&[[10, 20, 30], [40, 50, 61]]), 2, 1);
137 assert_ne!(a.id, b.id);
138 }
139
140 #[test]
142 fn the_same_bytes_at_different_dimensions_get_different_ids() {
143 let wide = ImageData::new(
144 opaque(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),
145 4,
146 1,
147 );
148 let square = ImageData::new(
149 opaque(&[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),
150 2,
151 2,
152 );
153 assert_ne!(wide.id, square.id);
154 }
155
156 #[test]
158 fn both_constructors_address_the_same_finished_image_alike() {
159 let half_alpha = vec![200, 100, 50, 128];
160 let mut premultiplied = half_alpha.clone();
161 premultiply_rgba(&mut premultiplied);
162 assert_eq!(
163 ImageData::new(half_alpha, 1, 1).id,
164 ImageData::from_premultiplied(premultiplied, 1, 1).id
165 );
166 }
167}