bayer_dithering_matrix/
lib.rs

1use core::mem;
2
3#[inline]
4const fn interleave_and_reverse_bits(a: usize, b: usize, highest_output_bit: u32) -> usize {
5    let mut acc: usize = 0;
6    let mut bit_o: usize = 1 << highest_output_bit;
7    // NOTE that this uses at most the lower half of the bits in a `usize`:
8    let mut bit_i: usize = 1;
9    loop {
10        if (a & bit_i) != 0 {
11            acc |= bit_o;
12        }
13        bit_o = bit_o.unbounded_shr(1);
14
15        if (b & bit_i) != 0 {
16            acc |= bit_o;
17        }
18        bit_o = bit_o.unbounded_shr(1);
19
20        if matches!(bit_o, 0) {
21            return acc;
22        }
23
24        bit_i = bit_i.unbounded_shl(1);
25    }
26}
27
28#[inline]
29pub const fn compute_value_at_index(i: usize, j: usize, highest_output_bit: u32) -> usize {
30    // https://en.wikipedia.org/wiki/Ordered_dithering#Threshold_map
31    interleave_and_reverse_bits(i ^ j, i, highest_output_bit)
32}
33
34#[inline]
35pub const fn matrix<T: Copy, const N: usize, const M: usize>() -> [[T; M]; N] {
36    assert!(N > 0, "Cannot generate a zero-size Bayer dithering matrix.");
37    assert!(M > 0, "Cannot generate a zero-size Bayer dithering matrix.");
38
39    let mut uninit = [[mem::MaybeUninit::uninit(); M]; N];
40    let mut ptr = uninit.as_mut_ptr() as *mut mem::MaybeUninit<T>;
41
42    let index_bits_rounding_up = {
43        let bits_rounding_up_n = ((N << 1) - 1).ilog2();
44        let bits_rounding_up_m = ((M << 1) - 1).ilog2();
45        let mut max = if bits_rounding_up_n > bits_rounding_up_m {
46            bits_rounding_up_n
47        } else {
48            bits_rounding_up_m
49        };
50        max <<= 1;
51        if let Some(sub) = max.checked_sub(1) {
52            sub
53        } else {
54            0
55        }
56    };
57
58    let just_past_max_representable = 1_usize.checked_shl((mem::size_of::<T>() << 3) as u32);
59
60    let mut i = 0;
61    loop {
62        let mut j = 0;
63        'j: loop {
64            let entire_usize = compute_value_at_index(i, j, index_bits_rounding_up);
65
66            #[cfg(debug_assertions)]
67            if let Some(just_past_max_representable) = just_past_max_representable {
68                assert!(
69                    entire_usize < just_past_max_representable,
70                    "It seems that the type you're using for Bayer matrix elements is too small to hold their values."
71                );
72            }
73
74            let lower_bits = &entire_usize as *const usize as *const T;
75
76            #[cfg(target_endian = "big")]
77            {
78                lower_bits = lower_bits
79                    .byte_add(const { core::mem::size_of::<usize>() - core::mem::size_of::<T>() });
80            }
81
82            let _: &mut _ = unsafe { &mut *ptr }.write(unsafe { *lower_bits });
83            if j == const { M - 1 } {
84                if i == const { N - 1 } {
85                    return unsafe {
86                        *(&uninit as *const [[mem::MaybeUninit<T>; M]; N] as *const [[T; M]; N])
87                    };
88                }
89                break 'j;
90            }
91            ptr = unsafe { ptr.add(1) };
92            j += 1;
93        }
94        ptr = unsafe { ptr.add(1) };
95        i += 1;
96    }
97}
98
99#[cfg(test)]
100mod test {
101    use super::*;
102
103    #[test]
104    fn test_1x1() {
105        const MATRIX: [[u8; 1]; 1] = matrix();
106        assert_eq!(MATRIX, [[0]]);
107    }
108
109    #[test]
110    fn test_2x2() {
111        const MATRIX: [[u8; 2]; 2] = matrix();
112        assert_eq!(MATRIX, [[0, 2], [3, 1]]);
113    }
114
115    #[test]
116    fn test_4x4() {
117        const MATRIX: [[u8; 4]; 4] = matrix();
118        assert_eq!(
119            MATRIX,
120            [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]],
121        );
122    }
123
124    #[test]
125    fn test_8x8() {
126        const MATRIX: [[u8; 8]; 8] = matrix();
127        assert_eq!(
128            MATRIX,
129            [
130                [0, 32, 8, 40, 2, 34, 10, 42],
131                [48, 16, 56, 24, 50, 18, 58, 26],
132                [12, 44, 4, 36, 14, 46, 6, 38],
133                [60, 28, 52, 20, 62, 30, 54, 22],
134                [3, 35, 11, 43, 1, 33, 9, 41],
135                [51, 19, 59, 27, 49, 17, 57, 25],
136                [15, 47, 7, 39, 13, 45, 5, 37],
137                [63, 31, 55, 23, 61, 29, 53, 21]
138            ],
139        );
140    }
141
142    /*
143    #[test]
144    fn test_16x16() {
145        const MATRIX: [[u8; 16]; 16] = matrix();
146        panic!("{MATRIX:?}");
147    }
148    */
149}