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(width: u16, height: u16, pixels: &[Rgb], palette: &[Rgb]) -> Vec<u8> {
66 let w = width as usize;
67 let h = height as usize;
68 let mut indices = vec![0u8; w * h];
69
70 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
71 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
72
73 let mut error_buf_l = vec![0.0f32; w * h];
74 let mut error_buf_a = vec![0.0f32; w * h];
75 let mut error_buf_b = vec![0.0f32; w * h];
76
77 let strength = 0.75f32;
78
79 for y in 0..h {
80 for x in 0..w {
81 let idx = y * w + x;
82 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
83 let current_lab = Lab {
84 l: (original_lab.l + error_buf_l[idx]).clamp(0.0, 100.0),
85 a: (original_lab.a + error_buf_a[idx]).clamp(-128.0, 127.0),
86 b: (original_lab.b + error_buf_b[idx]).clamp(-128.0, 127.0),
87 };
88
89 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
90 indices[idx] = color_idx as u8;
91
92 let best_color_lab = lab_palette[color_idx];
93 let err_l = (current_lab.l - best_color_lab.l) * strength;
94 let err_a = (current_lab.a - best_color_lab.a) * strength;
95 let err_b = (current_lab.b - best_color_lab.b) * strength;
96
97 let mut bufs = ErrorBuffers {
98 l: &mut error_buf_l,
99 a: &mut error_buf_a,
100 b: &mut error_buf_b,
101 };
102
103 if x + 1 < w {
104 diffuse(w, &mut bufs, x + 1, y, err_l, err_a, err_b, 7.0 / 16.0);
105 }
106 if y + 1 < h {
107 if x > 0 {
108 diffuse(w, &mut bufs, x - 1, y + 1, err_l, err_a, err_b, 3.0 / 16.0);
109 }
110 diffuse(w, &mut bufs, x, y + 1, err_l, err_a, err_b, 5.0 / 16.0);
111 if x + 1 < w {
112 diffuse(w, &mut bufs, x + 1, y + 1, err_l, err_a, err_b, 1.0 / 16.0);
113 }
114 }
115 }
116 }
117 indices
118}
119
120const BAYER_8X8: [u8; 64] = [
121 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,
122 28, 52, 20, 62, 30, 54, 22, 3, 35, 11, 43, 1, 33, 9, 41, 51, 19, 59, 27, 49, 17, 57, 25, 15,
123 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21,
124];
125
126pub fn dither_ordered(width: u16, height: u16, pixels: &[Rgb], palette: &[Rgb]) -> Vec<u8> {
128 let w = width as usize;
129 let h = height as usize;
130 let mut indices = vec![0u8; w * h];
131
132 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
133 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
134
135 let strength = 4.0f32;
137
138 for y in 0..h {
139 for x in 0..w {
140 let idx = y * w + x;
141 let bayer_val = BAYER_8X8[(y % 8) * 8 + (x % 8)] as f32 / 64.0;
142 let offset = (bayer_val - 0.5) * strength;
143
144 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
145 let current_lab = Lab {
146 l: (original_lab.l + offset).clamp(0.0, 100.0),
147 a: (original_lab.a + offset * 0.5).clamp(-128.0, 127.0),
148 b: (original_lab.b + offset * 0.5).clamp(-128.0, 127.0),
149 };
150
151 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
152 indices[idx] = color_idx as u8;
153 }
154 }
155 indices
156}
157
158pub fn dither_blue_noise(width: u16, height: u16, pixels: &[Rgb], palette: &[Rgb]) -> Vec<u8> {
160 let w = width as usize;
161 let h = height as usize;
162 let mut indices = vec![0u8; w * h];
163
164 let lab_palette: Vec<Lab> = palette.iter().map(|p| rgb_to_lab(p.r, p.g, p.b)).collect();
165 let planar_palette = PlanarLabPalette::from_lab(&lab_palette);
166
167 for y in 0..h {
168 for x in 0..w {
169 let idx = y * w + x;
170 let (ol, oa, ob) = get_blue_noise_offset(x as u16, y as u16);
171
172 let original_lab = rgb_to_lab(pixels[idx].r, pixels[idx].g, pixels[idx].b);
173 let current_lab = Lab {
174 l: (original_lab.l + ol).clamp(0.0, 100.0),
175 a: (original_lab.a + oa).clamp(-128.0, 127.0),
176 b: (original_lab.b + ob).clamp(-128.0, 127.0),
177 };
178
179 let color_idx = find_nearest_color_lab(current_lab, &planar_palette);
180 indices[idx] = color_idx as u8;
181 }
182 }
183 indices
184}
185
186#[inline]
188pub fn get_blue_noise_offset(x: u16, y: u16) -> (f32, f32, f32) {
189 let noise_val = BLUE_NOISE_32[((y % 32) as usize * 32) + (x % 32) as usize] as f32 / 255.0;
190 let noise_strength = 3.0f32;
191 let offset = (noise_val - 0.5) * noise_strength;
192 (offset, offset * 0.5, offset * 0.5)
193}
194
195struct ErrorBuffers<'a> {
196 l: &'a mut [f32],
197 a: &'a mut [f32],
198 b: &'a mut [f32],
199}
200
201#[inline]
202#[allow(clippy::too_many_arguments)]
203fn diffuse(
204 w: usize,
205 bufs: &mut ErrorBuffers,
206 x: usize,
207 y: usize,
208 el: f32,
209 ea: f32,
210 eb: f32,
211 weight: f32,
212) {
213 let idx = y * w + x;
214 bufs.l[idx] += el * weight;
215 bufs.a[idx] += ea * weight;
216 bufs.b[idx] += eb * weight;
217}