1use crate::{dimension::SpaceDimension, quant::MethodForDiffuse, Error, Result};
6
7pub type DitherFn = fn(&mut [u8], usize) -> Result<()>;
9
10pub fn dither_func_none(_data: &mut [u8], _width: usize) -> Result<()> {
12 Ok(())
13}
14
15pub fn dither_func_fs(data: &mut [u8], width: usize) -> Result<()> {
22 let data_len = data.len();
23 let max_offset = width * 3 + 3;
24
25 if max_offset > data_len {
26 Err(Error::Dither(format!(
27 "dither_func_fs: max offset({max_offset}) is out-of-bounds, max: {data_len}"
28 )))
29 } else {
30 let (error_r, error_g, error_b) = (data[0] & 0x7, data[1] & 0x7, data[2] & 0x7);
31
32 let mut offset = 3;
41
42 data[offset] = data[offset].saturating_add((error_r * 5) >> 4);
44 data[offset + 1] = data[offset + 1].saturating_add((error_g * 5) >> 4);
46 data[offset + 2] = data[offset + 2].saturating_add((error_b * 5) >> 4);
48
49 offset = width * offset - 3;
50
51 data[offset] = data[offset].saturating_add((error_r * 3) >> 4);
53 data[offset + 1] = data[offset + 1].saturating_add((error_g * 3) >> 4);
55 data[offset + 2] = data[offset + 2].saturating_add((error_b * 3) >> 4);
57
58 offset += 3;
59
60 data[offset] = data[offset].saturating_add((error_r * 5) >> 4);
62 data[offset + 1] = data[offset + 1].saturating_add((error_g * 5) >> 4);
64 data[offset + 2] = data[offset + 2].saturating_add((error_b * 5) >> 4);
66
67 Ok(())
68 }
69}
70
71pub fn dither_func_atkinson(data: &mut [u8], width: usize) -> Result<()> {
79 let data_len = data.len();
80 let max_offset = width * 6 + 3;
81 if max_offset > data_len {
82 Err(Error::Dither(format!(
83 "dither_func_fs: max offset({max_offset}) is out-of-bounds, max: {data_len}"
84 )))
85 } else {
86 let (error_r, error_g, error_b) = (
87 (data[0] & 0x7).saturating_add(4),
88 (data[1] & 0x7).saturating_add(4),
89 (data[2] & 0x7).saturating_add(4),
90 );
91
92 let (coeff_r, coeff_g, coeff_b) = (error_r >> 3, error_g >> 3, error_b >> 3);
93
94 let mut offset = 3;
95
96 data[offset] = data[offset].saturating_add(coeff_r);
98 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
100 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
102
103 offset *= 2;
104
105 data[offset] = data[offset].saturating_add(coeff_r);
107 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
109 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
111
112 offset += 3;
113
114 data[offset] = data[offset].saturating_add(coeff_r);
116 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
118 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
120
121 offset = (width - 1) * 3;
122
123 data[offset] = data[offset].saturating_add(coeff_r);
125 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
127 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
129
130 offset = width * 3;
131
132 data[offset] = data[offset].saturating_add(coeff_r);
134 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
136 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
138
139 offset = (width + 1) * 3;
140
141 data[offset] = data[offset].saturating_add(coeff_r);
143 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
145 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
147
148 offset = width * 6;
149
150 data[offset] = data[offset].saturating_add(coeff_r);
152 data[offset + 1] = data[offset + 1].saturating_add(coeff_g);
154 data[offset + 2] = data[offset + 2].saturating_add(coeff_b);
156
157 Ok(())
158 }
159}
160
161pub fn dither_func_jajuni(data: &mut [u8], width: usize) -> Result<()> {
169 let data_len = data.len();
170 let max_offset = (width * 2 + 2) * 3 + 3;
171 if max_offset > data_len {
172 Err(Error::Dither(format!(
173 "dither_func_fs: max offset({max_offset}) is out-of-bounds, max: {data_len}"
174 )))
175 } else {
176 let (error_r, error_g, error_b) = (data[0] & 0x7, data[1] & 0x7, data[2] & 0x7);
177
178 let (coeff_1_48_r, coeff_1_48_g, coeff_1_48_b) = (error_r / 48, error_g / 48, error_b / 48);
179 let (coeff_3_48_r, coeff_3_48_g, coeff_3_48_b) = (error_r / 16, error_g / 16, error_b / 16);
180 let (coeff_5_48_r, coeff_5_48_g, coeff_5_48_b) =
181 (error_r * 5 / 48, error_g * 5 / 48, error_b * 5 / 48);
182 let (coeff_7_48_r, coeff_7_48_g, coeff_7_48_b) =
183 (error_r * 7 / 48, error_g * 7 / 48, error_b * 7 / 48);
184
185 let mut offset = 3;
186
187 data[offset] = data[offset].saturating_add(coeff_7_48_r);
189 data[offset + 1] = data[offset + 1].saturating_add(coeff_7_48_g);
191 data[offset + 2] = data[offset + 2].saturating_add(coeff_7_48_b);
193
194 offset *= 2;
195
196 data[offset] = data[offset].saturating_add(coeff_5_48_r);
198 data[offset + 1] = data[offset + 1].saturating_add(coeff_5_48_g);
200 data[offset + 2] = data[offset + 2].saturating_add(coeff_5_48_b);
202
203 offset = (width - 2) * 3;
204
205 data[offset] = data[offset].saturating_add(coeff_3_48_r);
207 data[offset + 1] = data[offset + 1].saturating_add(coeff_3_48_g);
209 data[offset + 2] = data[offset + 2].saturating_add(coeff_3_48_b);
211
212 offset = (width - 1) * 3;
213
214 data[offset] = data[offset].saturating_add(coeff_5_48_r);
216 data[offset + 1] = data[offset + 1].saturating_add(coeff_5_48_g);
218 data[offset + 2] = data[offset + 2].saturating_add(coeff_5_48_b);
220
221 offset = width * 3;
222
223 data[offset] = data[offset].saturating_add(coeff_7_48_r);
225 data[offset + 1] = data[offset + 1].saturating_add(coeff_7_48_g);
227 data[offset + 2] = data[offset + 2].saturating_add(coeff_7_48_b);
229
230 offset = (width + 1) * 3;
231
232 data[offset] = data[offset].saturating_add(coeff_5_48_r);
234 data[offset + 1] = data[offset + 1].saturating_add(coeff_5_48_g);
236 data[offset + 2] = data[offset + 2].saturating_add(coeff_5_48_b);
238
239 offset = (width + 2) * 3;
240
241 data[offset] = data[offset].saturating_add(coeff_3_48_r);
243 data[offset + 1] = data[offset + 1].saturating_add(coeff_3_48_g);
245 data[offset + 2] = data[offset + 2].saturating_add(coeff_3_48_b);
247
248 offset = (width * 2 - 2) * 3;
249
250 data[offset] = data[offset].saturating_add(coeff_1_48_r);
252 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_48_g);
254 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_48_b);
256
257 offset = (width * 2 - 1) * 3;
258
259 data[offset] = data[offset].saturating_add(coeff_3_48_r);
261 data[offset + 1] = data[offset + 1].saturating_add(coeff_3_48_g);
263 data[offset + 2] = data[offset + 2].saturating_add(coeff_3_48_b);
265
266 offset = width * 6;
267
268 data[offset] = data[offset].saturating_add(coeff_5_48_r);
270 data[offset + 1] = data[offset + 1].saturating_add(coeff_5_48_g);
272 data[offset + 2] = data[offset + 2].saturating_add(coeff_5_48_b);
274
275 offset = (width * 2 + 1) * 3;
276
277 data[offset] = data[offset].saturating_add(coeff_3_48_r);
279 data[offset + 1] = data[offset + 1].saturating_add(coeff_3_48_g);
281 data[offset + 2] = data[offset + 2].saturating_add(coeff_3_48_b);
283
284 offset = (width * 2 + 2) * 3;
285
286 data[offset] = data[offset].saturating_add(coeff_1_48_r);
288 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_48_g);
290 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_48_b);
292
293 Ok(())
294 }
295}
296
297pub fn dither_func_stucki(data: &mut [u8], width: usize) -> Result<()> {
305 let data_len = data.len();
306 let max_offset = (width * 2 + 2) * 3 + 3;
307 if max_offset > data_len {
308 Err(Error::Dither(format!(
309 "dither_func_fs: max offset({max_offset}) is out-of-bounds, max: {data_len}"
310 )))
311 } else {
312 let (error_r, error_g, error_b) = (data[0] & 0x7, data[1] & 0x7, data[2] & 0x7);
313
314 let (coeff_1_48_r, coeff_1_48_g, coeff_1_48_b) = (error_r / 48, error_g / 48, error_b / 48);
315 let (coeff_2_48_r, coeff_2_48_g, coeff_2_48_b) = (error_r / 24, error_g / 24, error_b / 24);
316 let (coeff_4_48_r, coeff_4_48_g, coeff_4_48_b) = (error_r / 12, error_g / 12, error_b / 12);
317 let (coeff_8_48_r, coeff_8_48_g, coeff_8_48_b) = (error_r / 6, error_g / 6, error_b / 6);
318
319 let mut offset = 3;
320
321 data[offset] = data[offset].saturating_add(coeff_8_48_r);
323 data[offset + 1] = data[offset + 1].saturating_add(coeff_8_48_g);
325 data[offset + 2] = data[offset + 2].saturating_add(coeff_8_48_b);
327
328 offset *= 2;
329
330 data[offset] = data[offset].saturating_add(coeff_4_48_r);
332 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_48_g);
334 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_48_b);
336
337 offset = (width - 2) * 3;
338
339 data[offset] = data[offset].saturating_add(coeff_2_48_r);
341 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_48_g);
343 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_48_b);
345
346 offset = (width - 1) * 3;
347
348 data[offset] = data[offset].saturating_add(coeff_4_48_r);
350 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_48_g);
352 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_48_b);
354
355 offset = width * 3;
356
357 data[offset] = data[offset].saturating_add(coeff_8_48_r);
359 data[offset + 1] = data[offset + 1].saturating_add(coeff_8_48_g);
361 data[offset + 2] = data[offset + 2].saturating_add(coeff_8_48_b);
363
364 offset = (width + 1) * 3;
365
366 data[offset] = data[offset].saturating_add(coeff_4_48_r);
368 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_48_g);
370 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_48_b);
372
373 offset = (width + 2) * 3;
374
375 data[offset] = data[offset].saturating_add(coeff_2_48_r);
377 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_48_g);
379 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_48_b);
381
382 offset = (width * 2 - 2) * 3;
383
384 data[offset] = data[offset].saturating_add(coeff_1_48_r);
386 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_48_g);
388 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_48_b);
390
391 offset = (width * 2 - 1) * 3;
392
393 data[offset] = data[offset].saturating_add(coeff_2_48_r);
395 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_48_g);
397 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_48_b);
399
400 offset = width * 6;
401
402 data[offset] = data[offset].saturating_add(coeff_4_48_r);
404 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_48_g);
406 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_48_b);
408
409 offset = (width * 2 + 1) * 3;
410
411 data[offset] = data[offset].saturating_add(coeff_2_48_r);
413 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_48_g);
415 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_48_b);
417
418 offset = (width * 2 + 2) * 3;
419
420 data[offset] = data[offset].saturating_add(coeff_1_48_r);
422 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_48_g);
424 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_48_b);
426
427 Ok(())
428 }
429}
430
431pub fn dither_func_burkes(data: &mut [u8], width: usize) -> Result<()> {
438 let data_len = data.len();
439 let max_offset = (width + 2) * 3 + 3;
440 if max_offset > data_len {
441 Err(Error::Dither(format!(
442 "dither_func_fs: max offset({max_offset}) is out-of-bounds, max: {data_len}"
443 )))
444 } else {
445 let (error_r, error_g, error_b) = (data[0] & 0x7, data[1] & 0x7, data[2] & 0x7);
446
447 let (coeff_1_16_r, coeff_1_16_g, coeff_1_16_b) = (error_r / 16, error_g / 16, error_b / 16);
448 let (coeff_2_16_r, coeff_2_16_g, coeff_2_16_b) = (error_r / 8, error_g / 8, error_b / 8);
449 let (coeff_4_16_r, coeff_4_16_g, coeff_4_16_b) = (error_r / 4, error_g / 4, error_b / 4);
450
451 let mut offset = 3;
452
453 data[offset] = data[offset].saturating_add(coeff_4_16_r);
455 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_16_g);
457 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_16_b);
459
460 offset *= 2;
461
462 data[offset] = data[offset].saturating_add(coeff_2_16_r);
464 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_16_g);
466 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_16_b);
468
469 offset = (width - 2) * 3;
470
471 data[offset] = data[offset].saturating_add(coeff_1_16_r);
473 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_16_g);
475 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_16_b);
477
478 offset = (width - 1) * 3;
479
480 data[offset] = data[offset].saturating_add(coeff_2_16_r);
482 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_16_g);
484 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_16_b);
486
487 offset = width * 3;
488
489 data[offset] = data[offset].saturating_add(coeff_4_16_r);
491 data[offset + 1] = data[offset + 1].saturating_add(coeff_4_16_g);
493 data[offset + 2] = data[offset + 2].saturating_add(coeff_4_16_b);
495
496 offset = (width + 1) * 3;
497
498 data[offset] = data[offset].saturating_add(coeff_2_16_r);
500 data[offset + 1] = data[offset + 1].saturating_add(coeff_2_16_g);
502 data[offset + 2] = data[offset + 2].saturating_add(coeff_2_16_b);
504
505 offset = (width + 2) * 3;
506
507 data[offset] = data[offset].saturating_add(coeff_1_16_r);
509 data[offset + 1] = data[offset + 1].saturating_add(coeff_1_16_g);
511 data[offset + 2] = data[offset + 2].saturating_add(coeff_1_16_b);
513
514 Ok(())
515 }
516}
517
518pub fn dither_func_a_dither(data: &mut [u8], _width: usize, x: usize, y: usize) -> Result<()> {
520 let data_len = data.len();
521
522 if data_len < 3 {
523 Err(Error::Dither(format!(
524 "depth(3) is out-of-bounds, max: {data_len}"
525 )))
526 } else {
527 for c in data.iter_mut().take(3) {
528 let mut mask = ((((x + *c as usize * 17) + y * 236) * 119) & 255) as f32;
529 mask = (mask - 128.0) / 256.0;
530 let value = (*c as f32) + mask;
531
532 *c = if (0.0..=255.0).contains(&value) {
533 value as u8
534 } else if value < 0.0
535 || value.is_nan()
536 || (value.is_infinite() && value.is_sign_negative())
537 {
538 0
539 } else if value > 255.0 || (value.is_infinite() && value.is_sign_positive()) {
540 255
541 } else {
542 0
543 };
544 }
545
546 Ok(())
547 }
548}
549
550pub fn dither_func_x_dither(data: &mut [u8], _width: usize, x: usize, y: usize) -> Result<()> {
552 let data_len = data.len();
553
554 if data_len < 3 {
555 Err(Error::Dither(format!(
556 "depth(3) is out-of-bounds, max: {data_len}"
557 )))
558 } else {
559 for c in data.iter_mut().take(3) {
560 let mut mask = ((((x + *c as usize * 17) + y * 236) * 1234) & 511) as f32;
561 mask = (mask - 128.0) / 512.0;
562 let value = (*c as f32) + mask;
563
564 *c = if (0.0..=255.0).contains(&value) {
565 value as u8
566 } else if value < 0.0
567 || value.is_nan()
568 || (value.is_infinite() && value.is_sign_negative())
569 {
570 0
571 } else if value > 255.0 || (value.is_infinite() && value.is_sign_positive()) {
572 255
573 } else {
574 0
575 };
576 }
577
578 Ok(())
579 }
580}
581
582pub fn apply_15bpp_dither(
584 pixels: &mut [u8],
585 sd: SpaceDimension,
586 method_for_diffuse: MethodForDiffuse,
587) -> Result<()> {
588 let (x, y, width, height) = (sd.x, sd.y, sd.width, sd.height);
589
590 match method_for_diffuse {
591 MethodForDiffuse::Fs if x < width.saturating_sub(1) && y < height.saturating_sub(1) => {
592 dither_func_fs(pixels, width)
593 }
594 MethodForDiffuse::Atkinson
595 if x < width.saturating_sub(2) && y < height.saturating_sub(2) =>
596 {
597 dither_func_atkinson(pixels, width)
598 }
599 MethodForDiffuse::Jajuni if x < width.saturating_sub(2) && y < height.saturating_sub(2) => {
600 dither_func_jajuni(pixels, width)
601 }
602 MethodForDiffuse::Stucki if x < width.saturating_sub(2) && y < height.saturating_sub(2) => {
603 dither_func_stucki(pixels, width)
604 }
605 MethodForDiffuse::Burkes if x < width.saturating_sub(2) && y < height.saturating_sub(2) => {
606 dither_func_burkes(pixels, width)
607 }
608 MethodForDiffuse::ADither => dither_func_a_dither(pixels, width, x, y),
609 MethodForDiffuse::XDither => dither_func_x_dither(pixels, width, x, y),
610 _ => dither_func_none(pixels, width),
611 }
612}