1use dilate::DilateExpand;
10use image::Rgba;
11use image::RgbaImage;
12use kiddo::float::kdtree::KdTree;
13use palette::Lab;
14use palette::color_difference::EuclideanDistance;
15use rayon::iter::IndexedParallelIterator;
16use rayon::iter::IntoParallelIterator;
17use rayon::iter::IntoParallelRefMutIterator;
18use rayon::iter::ParallelIterator;
19use rayon::slice::ParallelSliceMut;
20use sobol_burley::sample;
21
22use crate::private;
23use crate::rgba_to_lab;
24
25pub trait PaletteBucketer: Sync + private::Sealed {
28 fn nearest(
30 &self,
31 point: &[f32; 3],
32 ) -> usize;
33
34 fn nearest_two(
39 &self,
40 point: Rgba<u8>,
41 ) -> [(usize, f32); 2];
42
43 fn nearest_rgb(
48 &self,
49 pixel: Rgba<u8>,
50 ) -> usize {
51 let lab = rgba_to_lab(pixel);
52 self.nearest(lab.as_ref())
53 }
54}
55
56pub struct KdTreeBucketer(KdTree<f32, usize, 3, 257, u32>);
59
60impl KdTreeBucketer {
61 pub fn new(palette: &[Lab]) -> Self {
62 let mut tree = KdTree::with_capacity(palette.len());
63 for (idx, color) in palette.iter().enumerate() {
64 tree.add(color.as_ref(), idx);
65 }
66 Self(tree)
67 }
68}
69
70impl private::Sealed for KdTreeBucketer {}
71
72impl PaletteBucketer for KdTreeBucketer {
73 fn nearest(
74 &self,
75 point: &[f32; 3],
76 ) -> usize {
77 self.0.nearest_one::<kiddo::SquaredEuclidean>(point).item
78 }
79
80 fn nearest_two(
81 &self,
82 point: Rgba<u8>,
83 ) -> [(usize, f32); 2] {
84 let point = rgba_to_lab(point);
85 let point = [point.l, point.a, point.b];
86 let [l1, l2] = self
87 .0
88 .nearest_n::<kiddo::SquaredEuclidean>(&point, 2)
89 .try_into()
90 .unwrap();
91 [(l1.item, l1.distance), (l2.item, l2.distance)]
92 }
93}
94
95pub trait Dither: private::Sealed {
98 const KERNEL: &[(isize, isize, f32)];
99 const DIV: f32;
100
101 fn dither_and_palettize(
104 image: &RgbaImage,
105 in_palette: &[Lab],
106 bucketer: &impl PaletteBucketer,
107 ) -> Vec<usize> {
108 let pixels = image.pixels().copied().map(rgba_to_lab).collect::<Vec<_>>();
109
110 let mut result = vec![0; image.width() as usize * image.height() as usize];
111
112 let mut spills = vec![[0.0; 3]; image.width() as usize * image.height() as usize];
113
114 for (idx, p) in pixels.iter().copied().enumerate() {
115 let pixel = <Lab>::new(
116 p.l + spills[idx][0],
117 p.a + spills[idx][1],
118 p.b + spills[idx][2],
119 );
120
121 let palette_idx = bucketer.nearest(&[pixel.l, pixel.a, pixel.b]);
122
123 let error0 = pixel.l - in_palette[palette_idx].l;
124 let error1 = pixel.a - in_palette[palette_idx].a;
125 let error2 = pixel.b - in_palette[palette_idx].b;
126 let spill = [error0, error1, error2];
127
128 result[idx] = palette_idx;
129
130 for (dx, dy, m) in Self::KERNEL {
131 let x = idx as isize % image.width() as isize + dx;
132 let y = idx as isize / image.width() as isize + dy;
133 if x < 0 || y < 0 || x >= image.width() as isize || y >= image.height() as isize {
134 continue;
135 }
136
137 let jdx = y * image.width() as isize + x;
138 let target = &mut spills[jdx as usize];
139 *target = [
140 target[0] + (spill[0] * m) / Self::DIV / 2.0,
141 target[1] + (spill[1] * m) / Self::DIV / 2.0,
142 target[2] + (spill[2] * m) / Self::DIV / 2.0,
143 ];
144 }
145 }
146
147 result
148 }
149}
150
151pub struct NoDither;
154impl private::Sealed for NoDither {}
155impl Dither for NoDither {
156 const DIV: f32 = 1.0;
157 const KERNEL: &[(isize, isize, f32)] = &[];
158
159 fn dither_and_palettize(
160 image: &RgbaImage,
161 _in_palette: &[Lab],
162 bucketer: &impl PaletteBucketer,
163 ) -> Vec<usize> {
164 let mut result = vec![0; image.width() as usize * image.height() as usize];
165 image.par_pixels().zip(&mut result).for_each(|(p, dest)| {
166 *dest = bucketer.nearest_rgb(*p);
167 });
168
169 result
170 }
171}
172
173pub struct Sierra;
174impl private::Sealed for Sierra {}
175impl Dither for Sierra {
176 const DIV: f32 = 32.0;
177 const KERNEL: &[(isize, isize, f32)] = &[
181 (1, 0, 5.0),
182 (2, 0, 3.0),
183 (-2, 1, 2.0),
185 (-1, 1, 4.0),
186 (0, 1, 5.0),
187 (1, 1, 4.0),
188 (2, 1, 2.0),
189 (-1, 2, 2.0),
191 (0, 2, 3.0),
192 (1, 2, 2.0),
193 ];
194}
195
196pub struct Sierra2;
197impl private::Sealed for Sierra2 {}
198impl Dither for Sierra2 {
199 const DIV: f32 = 16.0;
200 const KERNEL: &[(isize, isize, f32)] = &[
204 (1, 0, 4.0),
206 (2, 0, 3.0),
207 (-2, 1, 1.0),
209 (-1, 1, 2.0),
210 (0, 1, 3.0),
211 (1, 1, 2.0),
212 (2, 1, 1.0),
213 ];
214}
215
216pub struct SierraLite;
217impl private::Sealed for SierraLite {}
218impl Dither for SierraLite {
219 const DIV: f32 = 4.0;
220 const KERNEL: &[(isize, isize, f32)] = &[
223 (1, 0, 2.0),
225 (-1, 1, 1.0),
227 (0, 1, 1.0),
228 ];
229}
230
231pub struct FloydSteinberg;
232impl private::Sealed for FloydSteinberg {}
233impl Dither for FloydSteinberg {
234 const DIV: f32 = 16.0;
235 const KERNEL: &[(isize, isize, f32)] = &[
238 (1, 0, 7.0),
240 (-1, 1, 3.0),
242 (0, 1, 5.0),
243 (1, 1, 1.0),
244 ];
245}
246
247pub struct JJN;
248impl private::Sealed for JJN {}
249impl Dither for JJN {
250 const DIV: f32 = 48.0;
251 const KERNEL: &[(isize, isize, f32)] = &[
255 (1, 0, 7.0),
257 (2, 0, 5.0),
258 (-2, 1, 3.0),
260 (-1, 1, 5.0),
261 (0, 1, 7.0),
262 (1, 1, 5.0),
263 (2, 1, 3.0),
264 (-2, 2, 1.0),
266 (-1, 2, 2.0),
267 (0, 2, 5.0),
268 (1, 2, 3.0),
269 (2, 2, 1.0),
270 ];
271}
272
273pub struct Stucki;
274impl private::Sealed for Stucki {}
275impl Dither for Stucki {
276 const DIV: f32 = 42.0;
277 const KERNEL: &[(isize, isize, f32)] = &[
281 (1, 0, 8.0),
283 (2, 0, 4.0),
284 (-2, 1, 2.0),
286 (-1, 1, 4.0),
287 (0, 1, 8.0),
288 (1, 1, 4.0),
289 (2, 1, 2.0),
290 (-2, 2, 1.0),
292 (-1, 2, 2.0),
293 (0, 2, 4.0),
294 (1, 2, 2.0),
295 (2, 2, 1.0),
296 ];
297}
298
299pub struct Atkinson;
300impl private::Sealed for Atkinson {}
301impl Dither for Atkinson {
302 const DIV: f32 = 8.0;
303 const KERNEL: &[(isize, isize, f32)] = &[
307 (1, 0, 1.0),
309 (2, 0, 1.0),
310 (-1, 1, 1.0),
312 (0, 1, 1.0),
313 (1, 1, 1.0),
314 (0, 2, 1.0),
316 ];
317}
318
319pub struct Burkes;
320impl private::Sealed for Burkes {}
321impl Dither for Burkes {
322 const DIV: f32 = 32.0;
323 const KERNEL: &[(isize, isize, f32)] = &[
326 (1, 0, 8.0),
328 (2, 0, 4.0),
329 (-2, 1, 2.0),
331 (-1, 1, 4.0),
332 (0, 1, 8.0),
333 (1, 1, 4.0),
334 (2, 1, 2.0),
335 ];
336}
337
338pub struct Bayer;
339impl private::Sealed for Bayer {}
340impl Dither for Bayer {
341 const DIV: f32 = 0.0;
342 const KERNEL: &[(isize, isize, f32)] = &[];
343
344 fn dither_and_palettize(
345 image: &RgbaImage,
346 in_palette: &[Lab],
347 bucketer: &impl PaletteBucketer,
348 ) -> Vec<usize> {
349 let order = image.width().min(image.height()).ilog2().max(2) as usize;
350 let matrix_size = 1 << order;
351 let total_bits = 2 * order;
352 let mut matrix = vec![0.0; matrix_size * matrix_size];
353
354 (0..matrix_size as u32)
355 .into_par_iter()
356 .zip(matrix.par_chunks_mut(matrix_size))
357 .for_each(|(y, row)| {
358 for (x, cell) in row.iter_mut().enumerate() {
359 let x = x as u32;
360 let bits = (x ^ y).dilate_expand::<2>().value()
361 | (y.dilate_expand::<2>().value() << 1);
362 let bits = bits.reverse_bits() >> (u32::BITS - total_bits as u32);
363 *cell = bits as f32 / matrix_size.pow(2) as f32;
364 }
365 });
366
367 let max_matrix = matrix
368 .iter()
369 .copied()
370 .max_by(|l, r| l.total_cmp(r))
371 .unwrap_or(1.0);
372 let min_matrix = matrix
373 .iter()
374 .copied()
375 .min_by(|l, r| l.total_cmp(r))
376 .unwrap_or(0.0);
377 matrix.par_iter_mut().for_each(|cell| {
378 *cell = (*cell - min_matrix) / (max_matrix - min_matrix);
379 });
380
381 let mut result = vec![0; image.width() as usize * image.height() as usize];
382 image
383 .par_pixels()
384 .enumerate()
385 .zip(&mut result)
386 .for_each(|((idx, p), dest)| {
387 let x = (idx % image.width() as usize) % matrix_size;
388 let y = (idx / image.width() as usize) % matrix_size;
389
390 let [(l1_item, l1_dist), (l2_item, l2_dist)] = bucketer.nearest_two(*p);
391
392 let p_dist = in_palette[l1_item].distance_squared(in_palette[l2_item]);
393 let t = ((l1_dist - l2_dist + p_dist) / (2.0 * p_dist)).clamp(0.0, 1.0);
394
395 let m_idx = x + y * matrix_size;
396 if t > matrix[m_idx] {
397 *dest = l2_item;
398 } else {
399 *dest = l1_item;
400 }
401 });
402
403 result
404 }
405}
406
407pub struct Sobol;
411impl private::Sealed for Sobol {}
412impl Dither for Sobol {
413 const DIV: f32 = 0.0;
414 const KERNEL: &[(isize, isize, f32)] = &[];
415
416 fn dither_and_palettize(
417 image: &RgbaImage,
418 in_palette: &[Lab],
419 bucketer: &impl PaletteBucketer,
420 ) -> Vec<usize> {
421 let mut result = vec![0; image.width() as usize * image.height() as usize];
422 image
423 .par_pixels()
424 .enumerate()
425 .zip(&mut result)
426 .for_each(|((idx, p), dest)| {
427 let thresh = sample(idx as u32 % (1 << 16), 0, idx as u32 / (1 << 16));
428
429 let [(l1_item, l1_dist), (l2_item, l2_dist)] = bucketer.nearest_two(*p);
430
431 let p_dist = in_palette[l1_item].distance_squared(in_palette[l2_item]);
432 let t = ((l1_dist - l2_dist + p_dist) / (2.0 * p_dist)).clamp(0.0, 1.0);
433
434 if t > thresh {
435 *dest = l2_item;
436 } else {
437 *dest = l1_item;
438 }
439 });
440
441 result
442 }
443}