1use dilate::DilateExpand;
10use image::RgbImage;
11use kiddo::float::kdtree::KdTree;
12use palette::Lab;
13use rayon::{
14 iter::{
15 IndexedParallelIterator,
16 IntoParallelIterator,
17 ParallelIterator,
18 },
19 slice::ParallelSliceMut,
20};
21use sobol_burley::sample_4d;
22
23use crate::{
24 private,
25 rgb_to_lab,
26};
27
28pub trait Dither: private::Sealed {
31 const KERNEL: &[(isize, isize, f32)];
32 const DIV: f32;
33
34 fn dither_and_palettize(
42 image: &RgbImage,
43 in_palette: &[Lab],
44 target_palette_size: usize,
45 ) -> Vec<usize> {
46 let _ = target_palette_size;
47
48 let pixels = image.pixels().copied().map(rgb_to_lab).collect::<Vec<_>>();
49
50 let mut palette = KdTree::<_, _, 3, 257, u32>::with_capacity(in_palette.len());
51
52 for (idx, color) in in_palette.iter().enumerate() {
53 palette.add(color.as_ref(), idx);
54 }
55
56 let mut result = vec![0; image.width() as usize * image.height() as usize];
57
58 let mut spills = vec![[0.0; 3]; image.width() as usize * image.height() as usize];
59
60 for (idx, p) in pixels.iter().copied().enumerate() {
61 let pixel = <Lab>::new(
62 p.l + spills[idx][0],
63 p.a + spills[idx][1],
64 p.b + spills[idx][2],
65 );
66
67 let palette_idx =
68 palette.nearest_one::<kiddo::SquaredEuclidean>(&[pixel.l, pixel.a, pixel.b]);
69
70 let error0 = pixel.l - in_palette[palette_idx.item].l;
71 let error1 = pixel.a - in_palette[palette_idx.item].a;
72 let error2 = pixel.b - in_palette[palette_idx.item].b;
73 let spill = [error0, error1, error2];
74
75 result[idx] = palette_idx.item;
76
77 for (dx, dy, m) in Self::KERNEL {
78 let x = idx as isize % image.width() as isize + dx;
79 let y = idx as isize / image.width() as isize + dy;
80 if x < 0 || y < 0 || x >= image.width() as isize || y >= image.height() as isize {
81 continue;
82 }
83
84 let jdx = y * image.width() as isize + x;
85 let target = &mut spills[jdx as usize];
86 *target = [
87 target[0] + (spill[0] * m) / Self::DIV / 2.0,
88 target[1] + (spill[1] * m) / Self::DIV / 2.0,
89 target[2] + (spill[2] * m) / Self::DIV / 2.0,
90 ];
91 }
92 }
93
94 result
95 }
96}
97
98pub struct NoDither;
101impl private::Sealed for NoDither {}
102impl Dither for NoDither {
103 const DIV: f32 = 1.0;
104 const KERNEL: &[(isize, isize, f32)] = &[];
105
106 fn dither_and_palettize(image: &RgbImage, in_palette: &[Lab], _: usize) -> Vec<usize> {
107 let pixels = image.pixels().copied().map(rgb_to_lab).collect::<Vec<_>>();
108
109 let mut palette = KdTree::<_, _, 3, 32, u32>::with_capacity(in_palette.len());
110
111 for (idx, color) in in_palette.iter().enumerate() {
112 palette.add(color.as_ref(), idx);
113 }
114
115 let mut result = vec![0; image.width() as usize * image.height() as usize];
116 pixels
117 .into_par_iter()
118 .zip(&mut result)
119 .for_each(|(p, dest)| {
120 *dest = palette
121 .nearest_one::<kiddo::SquaredEuclidean>(&[p.l, p.a, p.b])
122 .item;
123 });
124
125 result
126 }
127}
128
129pub struct Sierra;
130impl private::Sealed for Sierra {}
131impl Dither for Sierra {
132 const DIV: f32 = 32.0;
133 const KERNEL: &[(isize, isize, f32)] = &[
137 (1, 0, 5.0),
138 (2, 0, 3.0),
139 (-2, 1, 2.0),
141 (-1, 1, 4.0),
142 (0, 1, 5.0),
143 (1, 1, 4.0),
144 (2, 1, 2.0),
145 (-1, 2, 2.0),
147 (0, 2, 3.0),
148 (1, 2, 2.0),
149 ];
150}
151
152pub struct Sierra2;
153impl private::Sealed for Sierra2 {}
154impl Dither for Sierra2 {
155 const DIV: f32 = 16.0;
156 const KERNEL: &[(isize, isize, f32)] = &[
160 (1, 0, 4.0),
162 (2, 0, 3.0),
163 (-2, 1, 1.0),
165 (-1, 1, 2.0),
166 (0, 1, 3.0),
167 (1, 1, 2.0),
168 (2, 1, 1.0),
169 ];
170}
171
172pub struct SierraLite;
173impl private::Sealed for SierraLite {}
174impl Dither for SierraLite {
175 const DIV: f32 = 4.0;
176 const KERNEL: &[(isize, isize, f32)] = &[
179 (1, 0, 2.0),
181 (-1, 1, 1.0),
183 (0, 1, 1.0),
184 ];
185}
186
187pub struct FloydSteinberg;
188impl private::Sealed for FloydSteinberg {}
189impl Dither for FloydSteinberg {
190 const DIV: f32 = 16.0;
191 const KERNEL: &[(isize, isize, f32)] = &[
194 (1, 0, 7.0),
196 (-1, 1, 3.0),
198 (0, 1, 5.0),
199 (1, 1, 1.0),
200 ];
201}
202
203pub struct JJN;
204impl private::Sealed for JJN {}
205impl Dither for JJN {
206 const DIV: f32 = 48.0;
207 const KERNEL: &[(isize, isize, f32)] = &[
211 (1, 0, 7.0),
213 (2, 0, 5.0),
214 (-2, 1, 3.0),
216 (-1, 1, 5.0),
217 (0, 1, 7.0),
218 (1, 1, 5.0),
219 (2, 1, 3.0),
220 (-2, 2, 1.0),
222 (-1, 2, 2.0),
223 (0, 2, 5.0),
224 (1, 2, 3.0),
225 (2, 2, 1.0),
226 ];
227}
228
229pub struct Stucki;
230impl private::Sealed for Stucki {}
231impl Dither for Stucki {
232 const DIV: f32 = 42.0;
233 const KERNEL: &[(isize, isize, f32)] = &[
237 (1, 0, 8.0),
239 (2, 0, 4.0),
240 (-2, 1, 2.0),
242 (-1, 1, 4.0),
243 (0, 1, 8.0),
244 (1, 1, 4.0),
245 (2, 1, 2.0),
246 (-2, 2, 1.0),
248 (-1, 2, 2.0),
249 (0, 2, 4.0),
250 (1, 2, 2.0),
251 (2, 2, 1.0),
252 ];
253}
254
255pub struct Atkinson;
256impl private::Sealed for Atkinson {}
257impl Dither for Atkinson {
258 const DIV: f32 = 8.0;
259 const KERNEL: &[(isize, isize, f32)] = &[
263 (1, 0, 1.0),
265 (2, 0, 1.0),
266 (-1, 1, 1.0),
268 (0, 1, 1.0),
269 (1, 1, 1.0),
270 (0, 2, 1.0),
272 ];
273}
274
275pub struct Burkes;
276impl private::Sealed for Burkes {}
277impl Dither for Burkes {
278 const DIV: f32 = 32.0;
279 const KERNEL: &[(isize, isize, f32)] = &[
282 (1, 0, 8.0),
284 (2, 0, 4.0),
285 (-2, 1, 2.0),
287 (-1, 1, 4.0),
288 (0, 1, 8.0),
289 (1, 1, 4.0),
290 (2, 1, 2.0),
291 ];
292}
293
294pub struct Bayer;
295impl private::Sealed for Bayer {}
296impl Dither for Bayer {
297 const DIV: f32 = 0.0;
298 const KERNEL: &[(isize, isize, f32)] = &[];
299
300 fn dither_and_palettize(
301 image: &RgbImage,
302 in_palette: &[Lab],
303 target_palette_size: usize,
304 ) -> Vec<usize> {
305 let r = (1usize << 8).div_ceil(target_palette_size.div_ceil(3));
306 let matrix_size = r.next_power_of_two();
307 let r = r as f32;
308 let mut l_matrix = vec![0.0; matrix_size * matrix_size];
309 let mut a_matrix = vec![0.0; matrix_size * matrix_size];
310 let mut b_matrix = vec![0.0; matrix_size * matrix_size];
311
312 (0..matrix_size as u32)
313 .into_par_iter()
314 .zip(l_matrix.par_chunks_mut(matrix_size))
315 .zip(a_matrix.par_chunks_mut(matrix_size))
316 .zip(b_matrix.par_chunks_mut(matrix_size))
317 .for_each(|(((y, row_l), row_a), row_b)| {
318 for (x, ((cell_l, cell_a), cell_b)) in
319 row_l.iter_mut().zip(row_a).zip(row_b).enumerate()
320 {
321 let x = x as u32;
322 let bits = (x ^ y).dilate_expand::<2>().value()
323 | (y.dilate_expand::<2>().value() << 1);
324 let bits = bits.reverse_bits() >> bits.leading_zeros();
325 let partial_thresh = bits as f32 / matrix_size.pow(2) as f32;
326
327 *cell_l = partial_thresh - 0.5;
328 *cell_a = partial_thresh - 0.5;
329 *cell_b = partial_thresh - 0.5;
330 }
331 });
332
333 let mut palette = KdTree::<_, _, 3, 257, u32>::with_capacity(in_palette.len());
334 for (idx, color) in in_palette.iter().enumerate() {
335 palette.add(color.as_ref(), idx);
336 }
337
338 let mut result = vec![0; image.width() as usize * image.height() as usize];
339 let pixels = image.pixels().copied().map(rgb_to_lab).collect::<Vec<_>>();
340
341 pixels
342 .into_par_iter()
343 .enumerate()
344 .zip(&mut result)
345 .for_each(|((idx, p), dest)| {
346 let x = (idx % image.width() as usize) % matrix_size;
347 let y = (idx / image.width() as usize) % matrix_size;
348
349 let m_idx = x + y * matrix_size;
350 let l = p.l + r * l_matrix[m_idx];
351 let a = p.a + r * a_matrix[m_idx];
352 let b = p.b + r * b_matrix[m_idx];
353
354 *dest = palette
355 .nearest_one::<kiddo::SquaredEuclidean>(&[l, a, b])
356 .item;
357 });
358
359 result
360 }
361}
362
363pub struct Sobol;
367impl private::Sealed for Sobol {}
368impl Dither for Sobol {
369 const DIV: f32 = 0.0;
370 const KERNEL: &[(isize, isize, f32)] = &[];
371
372 fn dither_and_palettize(
373 image: &RgbImage,
374 in_palette: &[Lab],
375 target_palette_size: usize,
376 ) -> Vec<usize> {
377 let r = (1usize << 8).div_ceil(target_palette_size * target_palette_size.ilog2() as usize)
378 as f32;
379
380 let mut palette = KdTree::<_, _, 3, 257, u32>::with_capacity(in_palette.len());
381 for (idx, color) in in_palette.iter().enumerate() {
382 palette.add(color.as_ref(), idx);
383 }
384
385 let mut result = vec![0; image.width() as usize * image.height() as usize];
386 let pixels = image.pixels().copied().map(rgb_to_lab).collect::<Vec<_>>();
387
388 pixels
389 .into_par_iter()
390 .enumerate()
391 .zip(&mut result)
392 .for_each(|((idx, p), dest)| {
393 let [l, a, b, _] = sample_4d(idx as u32 % (1 << 16), 0, idx as u32 / (1 << 16));
394
395 let l = p.l + (l - 0.5) * 2.0 * r;
396 let a = p.a + (a - 0.5) * 2.0 * r;
397 let b = p.b + (b - 0.5) * 2.0 * r;
398
399 *dest = palette
400 .nearest_one::<kiddo::SquaredEuclidean>(&[l, a, b])
401 .item;
402 });
403
404 result
405 }
406}