1use crate::color::{rgb_to_lab, Lab};
4use crate::quant::Rgb;
5use crate::simd::{find_nearest_color_lab, PlanarLabPalette};
6
7const BLUE_NOISE_32: [u8; 1024] = [
10 191, 14, 131, 231, 101, 12, 161, 42, 203, 115, 24, 142, 215, 56, 178, 89, 51, 245, 78, 168, 32,
11 212, 122, 253, 67, 182, 234, 10, 154, 226, 135, 198, 112, 145, 23, 194, 95, 151, 238, 84, 128,
12 219, 164, 48, 207, 72, 118, 37, 250, 62, 175, 108, 222, 1, 187, 138, 30, 242, 92, 157, 230,
13 104, 181, 211, 125, 235, 40, 148, 216, 75, 111, 254, 53, 197, 132, 21, 171, 81, 247, 15, 18,
14 162, 241, 87, 121, 190, 34, 225, 150, 60, 239, 100, 141, 213, 124, 177, 205, 94, 50, 228, 137,
15 174, 248, 11, 184, 218, 45, 159, 70, 252, 114, 31, 129, 214, 167, 43, 106, 232, 65, 144, 221,
16 19, 193, 127, 244, 82, 170, 201, 55, 240, 79, 180, 251, 91, 153, 236, 117, 172, 52, 210, 134,
17 227, 39, 147, 110, 143, 22, 202, 120, 224, 33, 160, 246, 8, 189, 103, 255, 68, 113, 196, 220,
18 64, 173, 97, 243, 139, 208, 74, 156, 233, 47, 140, 217, 126, 179, 86, 29, 192, 130, 211, 41,
19 165, 249, 107, 183, 223, 90, 152, 58, 241, 102, 237, 155, 234, 105, 119, 253, 17, 195, 133,
20 204, 36, 169, 229, 116, 186, 21, 149, 71, 206, 83, 176, 242, 146, 221, 54, 111, 251, 93, 158,
21 238, 66, 199, 123, 128, 38, 215, 110, 61, 236, 136, 185, 212, 14, 163, 245, 101, 172, 44, 209,
22 248, 166, 244, 194, 96, 150, 230, 10, 188, 222, 118, 49, 203, 77, 131, 254, 114, 225, 35, 142,
23 217, 73, 161, 247, 134, 226, 57, 178, 88, 213, 121, 197, 59, 239, 104, 181, 252, 92, 154, 233,
24 46, 147, 210, 125, 240, 16, 168, 231, 112, 145, 25, 205, 138, 228, 12, 184, 219, 100, 164, 246,
25 85, 191, 132, 202, 249, 69, 174, 98, 243, 1, 159, 235, 51, 214, 127, 171, 224, 33, 115, 250,
26 130, 216, 41, 141, 211, 122, 193, 237, 106, 182, 223, 19, 149, 232, 63, 176, 55, 241, 80, 169,
27 255, 109, 153, 221, 117, 173, 52, 208, 135, 227, 102, 195, 113, 144, 22, 204, 133, 225, 36,
28 160, 245, 10, 187, 234, 119, 253, 17, 170, 251, 67, 175, 95, 239, 136, 218, 76, 151, 229, 44,
29 143, 212, 124, 180, 89, 30, 192, 131, 213, 48, 166, 248, 105, 183, 222, 91, 155, 58, 244, 103,
30 236, 157, 235, 108, 120, 252, 15, 196, 139, 207, 32, 162, 230, 111, 185, 20, 148, 72, 203, 84,
31 177, 243, 147, 220, 53, 114, 254, 97, 152, 233, 65, 190, 126, 129, 39, 214, 116, 60, 237, 134,
32 186, 211, 13, 165, 246, 100, 171, 47, 209, 247, 167, 242, 193, 94, 150, 228, 11, 189, 221, 118,
33 50, 202, 78, 132, 255, 115, 226, 34, 140, 216, 74, 163, 249, 137, 227, 56, 179, 86, 215, 121,
34 198, 59, 240, 101, 181, 253, 93, 156, 234, 45, 146, 210, 125, 241, 18, 169, 231, 112, 144, 23,
35 206, 138, 229, 12, 182, 218, 99, 164, 245, 87, 194, 130, 203, 250, 68, 173, 96, 244, 2, 158,
36 232, 51, 212, 127, 170, 225, 31, 117, 251, 131, 217, 42, 142, 213, 123, 192, 238, 107, 183,
37 222, 20, 149, 233, 62, 177, 54, 242, 81, 168, 254, 108, 154, 220, 116, 174, 53, 207, 136, 226,
38 103, 196, 113, 145, 21, 205, 132, 224, 37, 161, 246, 9, 188, 235, 119, 252, 16, 171, 252, 66,
39 176, 94, 240, 135, 219, 75, 152, 228, 43, 144, 211, 124, 181, 88, 30, 191, 130, 212, 49, 167,
40 247, 104, 184, 223, 90, 156, 57, 243, 102, 237, 158, 234, 109, 121, 253, 14, 197, 138, 208, 33,
41 163, 231, 110, 186, 19, 147, 73, 204, 83, 178, 242, 146, 221, 52, 115, 255, 98, 153, 232, 64,
42 189, 127, 128, 40, 215, 117, 61, 236, 133, 185, 210, 12, 166, 245, 101, 172, 46, 209, 248, 168,
43 241, 194, 93, 151, 229, 10, 188, 220, 118, 51, 203, 79, 131, 254, 114, 227, 35, 141, 217, 74,
44 162, 250, 137, 226, 55, 180, 85, 216, 122, 199, 58, 239, 100, 182, 252, 92, 155, 233, 44, 145,
45 211, 126, 240, 17, 170, 230, 113, 143, 24, 207, 139, 228, 13, 181, 219, 98, 165, 244, 86, 195,
46 129, 202, 249, 69, 174, 97, 243, 1, 159, 231, 50, 213, 128, 171, 224, 32, 116, 251, 130, 218,
47 41, 143, 212, 123, 193, 237, 106, 184, 223, 21, 148, 232, 63, 177, 53, 242, 80, 169, 255, 109,
48 154, 221, 117, 173, 52, 208, 134, 227, 102, 196, 112, 144, 22, 204, 133, 225, 36, 160, 245, 11,
49 187, 234, 120, 253, 18, 170, 250, 67, 175, 95, 238, 136, 217, 76, 151, 229, 45, 142, 213, 124,
50 180, 90, 29, 192, 131, 211, 48, 166, 249, 105, 182, 222, 91, 155, 59, 244, 103, 236, 157, 235,
51 108, 119, 252, 15, 195, 139, 206, 33, 162, 230, 111, 185, 20, 149, 71, 203, 84, 176, 243, 147,
52 220, 54, 114, 254, 96, 152, 233, 66, 190, 125, 128, 38, 214, 116, 61, 237, 134, 186, 212, 14,
53 165, 246, 101, 172, 47, 208, 247, 167, 241, 194, 94, 150, 228, 10, 188, 221, 118, 49, 202, 77,
54 132, 255, 115, 226, 34, 140, 216, 73, 163, 248, 137, 227, 56, 179, 87, 215, 122, 197, 60, 240,
55 104, 181, 253, 92, 156, 234, 46, 146, 210, 126, 241, 17, 168, 231, 113, 144, 23, 205, 138, 229,
56 12, 183, 219, 99, 164, 245, 88, 193, 130, 203, 250, 68, 173, 98, 242, 2, 158, 232, 51, 214,
57 127, 171, 224, 31, 117, 251, 131, 217, 42, 141, 211, 123, 192, 238, 107, 182, 223, 19, 149,
58 233, 62, 176, 55, 242, 79, 169, 254, 108, 153, 222, 118, 174, 53, 207, 135, 226, 102, 195, 114,
59 145, 22, 204, 133, 225, 37, 160, 246, 9, 187, 235, 119, 252, 16, 172, 252, 65, 175, 95, 239,
60 136, 218, 75, 151, 228, 44, 143, 213, 124, 181, 89, 30, 191, 132, 212, 48, 167, 248, 104, 184,
61 222, 90, 156, 58, 243, 101, 237,
62];
63
64pub fn dither_floyd_steinberg(
66 width: u16,
67 height: u16,
68 pixels: &[Rgb],
69 palette: &[Rgb],
70 strength: f32,
71) -> Vec<u8> {
72 let w = width as usize;
73 let h = height as usize;
74 let mut indices = vec![0u8; w * h];
75
76 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
77 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
78
79 let mut error_buf_l = vec![0.0f32; w * h];
80 let mut error_buf_a = vec![0.0f32; w * h];
81 let mut error_buf_b = vec![0.0f32; w * h];
82
83 for y in 0..h {
84 for x in 0..w {
85 let idx = y * w + x;
86 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
87 let current_lab = Lab {
88 l: (original_lab.l + error_buf_l[idx]).clamp(0.0, 100.0),
89 a: (original_lab.a + error_buf_a[idx]).clamp(-128.0, 127.0),
90 b: (original_lab.b + error_buf_b[idx]).clamp(-128.0, 127.0),
91 };
92
93 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
94 indices[idx] = color_idx as u8;
95
96 let best_color_lab = lab_palette[color_idx];
97 let err_l = (current_lab.l - best_color_lab.l) * strength;
98 let err_a = (current_lab.a - best_color_lab.a) * strength;
99 let err_b = (current_lab.b - best_color_lab.b) * strength;
100
101 let mut bufs = ErrorBuffers {
102 l: &mut error_buf_l,
103 a: &mut error_buf_a,
104 b: &mut error_buf_b,
105 };
106
107 if x + 1 < w {
108 diffuse(w, &mut bufs, x + 1, y, err_l, err_a, err_b, 7.0 / 16.0);
109 }
110 if y + 1 < h {
111 if x > 0 {
112 diffuse(w, &mut bufs, x - 1, y + 1, err_l, err_a, err_b, 3.0 / 16.0);
113 }
114 diffuse(w, &mut bufs, x, y + 1, err_l, err_a, err_b, 5.0 / 16.0);
115 if x + 1 < w {
116 diffuse(w, &mut bufs, x + 1, y + 1, err_l, err_a, err_b, 1.0 / 16.0);
117 }
118 }
119 }
120 }
121 indices
122}
123
124const BAYER_8X8: [u8; 64] = [
125 0, 32, 8, 40, 2, 34, 10, 42, 48, 16, 56, 24, 50, 18, 58, 26, 12, 44, 4, 36, 14, 46, 6, 38, 60,
126 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27, 49, 17, 57, 25, 15,
127 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21,
128];
129
130pub fn dither_ordered(
132 width: u16,
133 height: u16,
134 pixels: &[Rgb],
135 palette: &[Rgb],
136 strength: f32,
137) -> Vec<u8> {
138 let w = width as usize;
139 let h = height as usize;
140 let mut indices = vec![0u8; w * h];
141
142 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
143 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
144
145 let spread = 4.0f32 * strength;
147
148 for y in 0..h {
149 for x in 0..w {
150 let idx = y * w + x;
151 let bayer_val = BAYER_8X8[(y % 8) * 8 + (x % 8)] as f32 / 64.0;
152 let offset = (bayer_val - 0.5) * spread;
153
154 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
155 let current_lab = Lab {
156 l: (original_lab.l + offset).clamp(0.0, 100.0),
157 a: (original_lab.a + offset * 0.5).clamp(-128.0, 127.0),
158 b: (original_lab.b + offset * 0.5).clamp(-128.0, 127.0),
159 };
160
161 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
162 indices[idx] = color_idx as u8;
163 }
164 }
165 indices
166}
167
168pub fn dither_blue_noise(
170 width: u16,
171 height: u16,
172 pixels: &[Rgb],
173 palette: &[Rgb],
174 strength: f32,
175) -> Vec<u8> {
176 let w = width as usize;
177 let h = height as usize;
178 let mut indices = vec![0u8; w * h];
179
180 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
181 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
182
183 for y in 0..h {
184 for x in 0..w {
185 let idx = y * w + x;
186 let (ol, oa, ob) = get_blue_noise_offset(x as u16, y as u16, strength);
187
188 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
189 let current_lab = Lab {
190 l: (original_lab.l + ol).clamp(0.0, 100.0),
191 a: (original_lab.a + oa).clamp(-128.0, 127.0),
192 b: (original_lab.b + ob).clamp(-128.0, 127.0),
193 };
194
195 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
196 indices[idx] = color_idx as u8;
197 }
198 }
199 indices
200}
201
202#[inline]
204pub fn get_blue_noise_offset(x: u16, y: u16, strength: f32) -> (f32, f32, f32) {
205 let noise_val = BLUE_NOISE_32[((y % 32) as usize * 32) + (x % 32) as usize] as f32 / 255.0;
206 let noise_intensity = 3.0f32 * strength;
207 let offset = (noise_val - 0.5) * noise_intensity;
208 (offset, offset * 0.5, offset * 0.5)
209}
210
211struct ErrorBuffers<'a> {
212 l: &'a mut [f32],
213 a: &'a mut [f32],
214 b: &'a mut [f32],
215}
216
217#[inline]
218#[allow(clippy::too_many_arguments)]
219fn diffuse(
220 w: usize,
221 bufs: &mut ErrorBuffers,
222 x: usize,
223 y: usize,
224 el: f32,
225 ea: f32,
226 eb: f32,
227 weight: f32,
228) {
229 let idx = y * w + x;
230 bufs.l[idx] += el * weight;
231 bufs.a[idx] += ea * weight;
232 bufs.b[idx] += eb * weight;
233}