image_effects/dither/
bayer.rs

1use ndarray::{concatenate, Array, Axis, Dim};
2use palette::Srgb;
3
4use crate::{utils::image::RgbImageRepr, colour::utils::quantize_rgb, effect::Effect};
5
6/// Represents the _ordered_ method of dithering. Compared to error propagation, this method is less accurate - however it
7/// results in a pattern that can be visually appealing.
8/// 
9/// In addition it only modifies each pixel on its own without needing to simultaneously touch/affect other pixels, making it 
10/// easily possible to parallellize.
11pub struct Bayer {
12    matrix_size: usize,
13    palette: Vec<Srgb>,
14}
15
16impl Bayer {
17
18    /// Creates a new `Bayer` ditherer with the given matrix size.
19    pub fn new(matrix_size: usize, palette: Vec<Srgb>) -> Self {
20        Self { matrix_size, palette }
21    }
22
23    /// Creates a clone of the ditherer with a different matrix size.
24    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}