image_effects/dither/
bayer.rs1use ndarray::{concatenate, Array, Axis, Dim};
2use palette::Srgb;
3
4use crate::{utils::image::RgbImageRepr, colour::utils::quantize_rgb, effect::Effect};
5
6pub struct Bayer {
12 matrix_size: usize,
13 palette: Vec<Srgb>,
14}
15
16impl Bayer {
17
18 pub fn new(matrix_size: usize, palette: Vec<Srgb>) -> Self {
20 Self { matrix_size, palette }
21 }
22
23 pub fn with_matrix_size(&self, matrix_size: usize) -> Self {
25 Self { matrix_size, palette: self.palette.clone() }
26 }
27
28 fn dither_matrix(n: usize) -> Array<f64, Dim<[usize; 2]>> {
29 if n == 1 {
30 return Array::<f64, _>::zeros((1, 1));
31 }
32
33 let nested_matrix = Self::dither_matrix(n / 2);
34 let multiplier = n.pow(2) as f64;
35
36 let first = multiplier * nested_matrix.clone();
37 let second = multiplier * nested_matrix.clone() + 2.;
38 let third = multiplier * nested_matrix.clone() + 3.;
39 let fourth = multiplier * nested_matrix.clone() + 1.;
40
41 let first_col = concatenate(Axis(0), &[first.view(), third.view()]).unwrap();
42 let second_col = concatenate(Axis(0), &[second.view(), fourth.view()]).unwrap();
43
44 (1. / multiplier) * concatenate(Axis(1), &[first_col.view(), second_col.view()]).unwrap()
45 }
46}
47
48impl Effect<RgbImageRepr> for Bayer {
49 fn affect(&self, mut image: RgbImageRepr) -> RgbImageRepr {
50 let matrix = Self::dither_matrix(self.matrix_size);
51
52 let ydim = image.len();
53 let xdim = image.get(0).map(|row| row.len()).unwrap_or(0);
54
55 for y in 0..ydim {
56 for x in 0..xdim {
57 let mut color = Srgb::from(image[y][x]).into_format::<f32>();
58
59 let offset = (1.0 / 3.0)
60 * (matrix
61 .get((x % self.matrix_size, y % self.matrix_size))
62 .unwrap_or(&0.0)
63 - 0.5) as f32;
64
65 color.red = color.red + offset;
66 color.blue = color.blue + offset;
67 color.green = color.green + offset;
68
69 image[y][x] = quantize_rgb(color, &self.palette).into_format().into();
70 }
71 }
72
73 image
74 }
75}