1use image::{ImageBuffer, Rgba, RgbaImage};
19
20fn sort_widest(pixels: &mut [Rgba<u8>]) {
21 let r_range =
22 pixels.iter().map(|p| p[0]).max().unwrap() - pixels.iter().map(|p| p[0]).min().unwrap();
23 let g_range =
24 pixels.iter().map(|p| p[1]).max().unwrap() - pixels.iter().map(|p| p[1]).min().unwrap();
25 let b_range =
26 pixels.iter().map(|p| p[2]).max().unwrap() - pixels.iter().map(|p| p[2]).min().unwrap();
27 let a_range =
28 pixels.iter().map(|p| p[3]).max().unwrap() - pixels.iter().map(|p| p[3]).min().unwrap();
29
30 if r_range > g_range && r_range > b_range && r_range > a_range {
31 pixels.sort_by_key(|p| p[0]);
32 } else if g_range > b_range && g_range > a_range {
33 pixels.sort_by_key(|p| p[1]);
34 } else if b_range > a_range {
35 pixels.sort_by_key(|p| p[2]);
36 } else {
37 pixels.sort_by_key(|p| p[3]);
38 }
39}
40
41pub fn build_palette<I>(n: usize, pixels: I) -> Vec<Rgba<u8>>
50where
51 I: IntoIterator<Item = Rgba<u8>>,
52{
53 let n = n.next_power_of_two();
54 let mut pixels: Vec<_> = pixels.into_iter().collect();
55 pixels.sort_by_key(|p| p.data);
56 pixels.dedup();
57
58 if n >= pixels.len() {
59 return pixels;
60 }
61
62 for i in 0..n.trailing_zeros() {
63 let i = 1 << i;
64 for j in 0..i {
65 let pixels_a = j * pixels.len() / i;
66 let pixels_b = (j + 1) * pixels.len() / i;
67 sort_widest(&mut pixels[pixels_a..pixels_b]);
68 }
69 }
70
71 let mut output = Vec::with_capacity(n);
72 for i in 0..n {
73 let pixels_a = i * pixels.len() / n;
74 let pixels_b = (i + 1) * pixels.len() / n;
75 let sub_pixels = &pixels[pixels_a..pixels_b];
76
77 let avg_r: usize =
78 sub_pixels.iter().map(|p| p[0] as usize).sum::<usize>() / sub_pixels.len();
79 let avg_g: usize =
80 sub_pixels.iter().map(|p| p[1] as usize).sum::<usize>() / sub_pixels.len();
81 let avg_b: usize =
82 sub_pixels.iter().map(|p| p[2] as usize).sum::<usize>() / sub_pixels.len();
83 let avg_a: usize =
84 sub_pixels.iter().map(|p| p[3] as usize).sum::<usize>() / sub_pixels.len();
85
86 output.push(Rgba {
87 data: [avg_r as u8, avg_g as u8, avg_b as u8, avg_a as u8],
88 });
89 }
90 output
91}
92
93pub fn quantize(image: &mut RgbaImage, palette: &[Rgba<u8>]) {
97 let mut fimage: ImageBuffer<Rgba<f32>, Vec<f32>> = ImageBuffer::from_raw(
98 image.width(),
99 image.height(),
100 vec![0.0; 4 * image.width() as usize * image.height() as usize],
101 )
102 .unwrap();
103
104 for x in 0..image.width() {
105 for y in 0..image.height() {
106 let [r, g, b, a] = image.get_pixel(x, y).data;
107 fimage.put_pixel(
108 x,
109 y,
110 Rgba {
111 data: [
112 r as f32 / 255.0,
113 g as f32 / 255.0,
114 b as f32 / 255.0,
115 a as f32 / 255.0,
116 ],
117 },
118 );
119 }
120 }
121
122 let palette: Vec<Rgba<f32>> = palette
123 .iter()
124 .map(|p| {
125 let [r, g, b, a] = p.data;
126 Rgba {
127 data: [
128 r as f32 / 255.0,
129 g as f32 / 255.0,
130 b as f32 / 255.0,
131 a as f32 / 255.0,
132 ],
133 }
134 })
135 .collect();
136
137 for x in 0..fimage.width() {
138 for y in 0..image.height() {
139 let old_pixel = fimage.get_pixel(x, y);
140 let [or, og, ob, oa] = old_pixel.data;
141
142 let new_pixel = palette
143 .iter()
144 .min_by_key(|p| {
145 let [nr, ng, nb, na] = p.data;
146 (((nr - or).powi(2)
147 + (ng - og).powi(2)
148 + (nb - ob).powi(2)
149 + (na - oa).powi(2))
150 * 1000000.0) as usize
151 })
152 .unwrap();
153 let [nr, ng, nb, na] = new_pixel.data;
154 let er = or - nr;
155 let eg = og - ng;
156 let eb = ob - nb;
157 let ea = oa - na;
158
159 if x < fimage.width() - 1 {
160 let [hr, hg, hb, ha] = &mut fimage.get_pixel_mut(x + 1, y).data;
161 *hr += er * 0.4375;
162 *hg += eg * 0.4375;
163 *hb += eb * 0.4375;
164 *ha += ea * 0.4375;
165 }
166 if y < fimage.height() - 1 {
167 let [hr, hg, hb, ha] = &mut fimage.get_pixel_mut(x, y + 1).data;
168 *hr += er * 0.1875;
169 *hg += eg * 0.1875;
170 *hb += eb * 0.1875;
171 *ha += ea * 0.1875;
172
173 if x < fimage.width() - 1 {
174 let [hr, hg, hb, ha] = &mut fimage.get_pixel_mut(x + 1, y + 1).data;
175 *hr += er * 0.3125;
176 *hg += eg * 0.3125;
177 *hb += eb * 0.3125;
178 *ha += ea * 0.3125;
179 }
180
181 if x > 0 {
182 let [hr, hg, hb, ha] = &mut fimage.get_pixel_mut(x - 1, y + 1).data;
183 *hr += er * 0.0625;
184 *hg += eg * 0.0625;
185 *hb += eb * 0.0625;
186 *ha += ea * 0.0625;
187 }
188 }
189
190 fimage.put_pixel(x, y, *new_pixel);
191 image.put_pixel(
192 x,
193 y,
194 Rgba {
195 data: [
196 (nr * 255.0) as u8,
197 (ng * 255.0) as u8,
198 (nb * 255.0) as u8,
199 (na * 255.0) as u8,
200 ],
201 },
202 );
203 }
204 }
205}