image_canvas/
bits.rs

1use crate::layout::{SampleBits, SampleParts};
2use image_texel::Texel;
3
4/// Specifies which bits a channel comes from, within a `TexelKind` aggregate.
5#[derive(Clone, Copy, Debug)]
6pub struct FromBits {
7    pub(crate) begin: usize,
8    pub(crate) len: usize,
9}
10
11macro_rules! from_bits {
12    ($bits:ident = { $($variant:pat => $($value:expr)+);* }) => {
13        match $bits {
14            $($variant => from_bits!(@ $($value);*)),*,
15        }
16    };
17    (@ $v0:expr) => {
18        [Some(FromBits::from_range($v0)), None, None, None, None, None, None, None]
19    };
20    (@ $v0:expr; $v1:expr) => {
21        [Some(FromBits::from_range($v0)), Some(FromBits::from_range($v1)), None, None, None, None, None, None]
22    };
23    (@ $v0:expr; $v1:expr; $v2:expr) => {
24        [
25            Some(FromBits::from_range($v0)),
26            Some(FromBits::from_range($v1)),
27            Some(FromBits::from_range($v2)),
28            None,
29            None,
30            None,
31            None,
32            None,
33        ]
34    };
35    (@ $v0:expr; $v1:expr; $v2:expr; $v3:expr) => {
36        [
37            Some(FromBits::from_range($v0)),
38            Some(FromBits::from_range($v1)),
39            Some(FromBits::from_range($v2)),
40            Some(FromBits::from_range($v3)),
41            None,
42            None,
43            None,
44            None,
45        ]
46    };
47    (@ $v0:expr; $v1:expr; $v2:expr; $v3:expr; $v4:expr; $v5:expr) => {
48        [
49            Some(FromBits::from_range($v0)),
50            Some(FromBits::from_range($v1)),
51            Some(FromBits::from_range($v2)),
52            Some(FromBits::from_range($v3)),
53            Some(FromBits::from_range($v4)),
54            Some(FromBits::from_range($v5)),
55            None,
56            None,
57        ]
58    };
59    (@ $v0:expr; $v1:expr; $v2:expr; $v3:expr; $v4:expr; $v5:expr; $v6:expr; $v7:expr) => {
60        [
61            Some(FromBits::from_range($v0)),
62            Some(FromBits::from_range($v1)),
63            Some(FromBits::from_range($v2)),
64            Some(FromBits::from_range($v3)),
65            Some(FromBits::from_range($v4)),
66            Some(FromBits::from_range($v5)),
67            Some(FromBits::from_range($v6)),
68            Some(FromBits::from_range($v7)),
69        ]
70    };
71}
72
73impl FromBits {
74    const NO_BITS: Self = FromBits { begin: 0, len: 0 };
75
76    const fn from_range(range: core::ops::Range<usize>) -> Self {
77        FromBits {
78            begin: range.start,
79            len: range.end - range.start,
80        }
81    }
82
83    pub(crate) fn for_pixel(bits: SampleBits, parts: SampleParts) -> [Self; 4] {
84        let mut vals = [Self::NO_BITS; 4];
85
86        let bits = Self::bits(bits);
87        let channels = parts.channels();
88
89        for (bits, (channel, pos)) in bits.zip(channels) {
90            if let Some(_) = channel {
91                vals[pos as usize] = bits;
92            }
93        }
94
95        vals
96    }
97
98    pub(crate) fn for_pixels<const N: usize>(
99        bits: SampleBits,
100        parts: SampleParts,
101    ) -> [[Self; 4]; N] {
102        let mut vals = [[Self::NO_BITS; 4]; N];
103
104        let mut bits = Self::bits(bits);
105
106        for vals in vals.iter_mut() {
107            let channels = parts.channels().filter_map(|(ch, p)| Some((ch?, p)));
108
109            for (_, pos) in channels {
110                if let Some(bits) = bits.next() {
111                    vals[pos as usize] = bits;
112                }
113            }
114        }
115
116        vals
117    }
118
119    pub(crate) const fn mask(self) -> u32 {
120        ((-1i64 as u64) ^ u32::MAX as u64).rotate_left(self.len as u32) as u32
121    }
122
123    fn bits(bits: SampleBits) -> impl Iterator<Item = Self> {
124        use SampleBits::*;
125
126        let filled: [Option<Self>; 8] = from_bits!(bits = {
127            Int8 | UInt8 => 0..8;
128            UInt332 => 0..3 3..6 6..8;
129            UInt233 => 0..2 2..5 5..8;
130            Int16 | UInt16 => 0..16;
131            UInt4x2 => 0..4 4..8;
132            UInt4x4 => 0..4 4..8 8..12 12..16;
133            UInt4x6 => 0..4 4..8 8..12 12..16 16..20 20..24;
134            UInt_444 => 4..8 8..12 12..16;
135            UInt444_ => 0..4 4..8 8..12;
136            UInt565 => 0..5 5..11 11..16;
137            Int8x2 | UInt8x2 => 0..8 8..16;
138            Int8x3 | UInt8x3 => 0..8 8..16 16..24;
139            Int8x4 | UInt8x4 => 0..8 8..16 16..24 24..32;
140            UInt8x6 => 0..8 8..16 16..24 24..32 32..40 40..48;
141            Int16x2 | UInt16x2 => 0..16 16..32;
142            Int16x3 | UInt16x3 => 0..16 16..32 32..48;
143            Int16x4 | UInt16x4 => 0..16 16..32 32..48 48..64;
144            UInt16x6 => 0..16 16..32 32..48 48..64 64..80 80..96;
145            UInt1010102 => 0..10 10..20 20..30 30..32;
146            UInt2101010 => 0..2 2..12 12..22 22..32;
147            UInt101010_ => 0..10 10..20 20..30;
148            UInt_101010 => 2..12 12..22 22..32;
149            Float16x4 => 0..16 16..32 32..48 48..64;
150            Float32 => 0..32;
151            Float32x2 => 0..32 32..64;
152            Float32x3 => 0..32 32..64 64..96;
153            Float32x4 => 0..32 32..64 64..96 96..128;
154            Float32x6 => 0..32 32..64 64..96 96..128 128..160 160..192;
155            UInt1x8 => 0..1 1..2 2..3 3..4 4..5 5..6 6..7 7..8;
156            UInt2x4 => 0..2 2..4 4..6 6..8
157        });
158
159        filled.into_iter().filter_map(|x| x)
160    }
161
162    /// Extract bit as a big-endian interpretation.
163    ///
164    /// The highest bit of each byte being the first. Returns a value as `u32` with the same
165    /// interpretation where the lowest bits are filled.
166    ///
167    /// FIXME: there's **a lot** of constant pre-processing. For example, if always access through
168    /// either 32-bit boundary or 64-bit boundary then the startu64 is also one of two constants.
169    #[inline]
170    pub(crate) fn extract_as_lsb<T>(&self, texel: Texel<T>, val: &T) -> u32 {
171        // FIXME(perf): vectorized form for all texels where possible.
172        // Grab up to 8 bytes surrounding the bits, convert using u64 intermediate, then shift
173        // upwards (by at most 7 bit) and mask off any remaining bits.
174        let ne_bytes = texel.to_bytes(core::slice::from_ref(val));
175        let start_byte = self.begin / 8;
176        let from_bytes = &ne_bytes[start_byte.min(ne_bytes.len())..];
177        assert!(self.len <= 32);
178
179        let shift = self.begin - start_byte * 8;
180        let bitlen = self.len + shift;
181        let copylen = if bitlen % 8 == 0 {
182            bitlen / 8
183        } else {
184            bitlen / 8 + 1
185        };
186
187        let mut be_bytes = [0; 8];
188        let initlen = copylen.min(8).min(from_bytes.len());
189        be_bytes[..initlen].copy_from_slice(&from_bytes[..initlen]);
190
191        let val = u64::from_be_bytes(be_bytes) >> (64 - bitlen).min(63);
192        // Start with a value where the 32-low bits are clear, high bits are set.
193        val as u32 & self.mask()
194    }
195
196    pub(crate) fn insert_as_lsb<T>(&self, texel: Texel<T>, val: &mut T, bits: u32) {
197        // FIXME(perf): vectorized form for all texels where possible.
198        let ne_bytes = texel.to_mut_bytes(core::slice::from_mut(val));
199        let start_byte = self.begin / 8;
200        let bytestart = start_byte.min(ne_bytes.len());
201        let texel_bytes = &mut ne_bytes[bytestart..];
202
203        let shift = self.begin - start_byte * 8;
204        let bitlen = self.len + shift;
205        let copylen = if bitlen % 8 == 0 {
206            bitlen / 8
207        } else {
208            bitlen / 8 + 1
209        };
210
211        let mut be_bytes = [0; 8];
212        let initlen = copylen.min(8).min(texel_bytes.len());
213        be_bytes[..initlen].copy_from_slice(&texel_bytes[..initlen]);
214
215        let mask = ((-1i64 as u64) ^ u32::MAX as u64).rotate_left((self.len as u32).min(32))
216            & (u32::MAX as u64);
217
218        let bitshift = (64 - bitlen).min(63);
219        let newval = (u64::from_be_bytes(be_bytes) & !(mask << bitshift))
220            | (u64::from(bits) & mask) << bitshift;
221
222        be_bytes = newval.to_be_bytes();
223        texel_bytes[..initlen].copy_from_slice(&be_bytes[..initlen]);
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::FromBits;
230    use image_texel::AsTexel;
231
232    #[test]
233    fn bit_extraction() {
234        fn extract_simple(r: core::ops::Range<usize>, val: &u8) -> u32 {
235            FromBits {
236                begin: r.start,
237                len: r.len(),
238            }
239            .extract_as_lsb(u8::texel(), &val)
240        }
241
242        let val = 0b1000_1010u8;
243        assert_eq!(extract_simple(0..1, &val), 1);
244        assert_eq!(extract_simple(1..2, &val), 0);
245        assert_eq!(extract_simple(2..3, &val), 0);
246        assert_eq!(extract_simple(6..7, &val), 1);
247        assert_eq!(extract_simple(0..7, &val), val as u32 >> 1);
248        assert_eq!(extract_simple(1..8, &val), (val & 0x7f) as u32);
249
250        assert_eq!(extract_simple(0..0, &val), 0);
251        assert_eq!(extract_simple(1..1, &val), 0);
252    }
253
254    #[test]
255    fn bit_insertion() {
256        // Return the binary diff of insertion.
257        fn insert_simple(r: core::ops::Range<usize>, bits: u32, val: &mut u8) -> u8 {
258            let before = *val;
259            FromBits {
260                begin: r.start,
261                len: r.len(),
262            }
263            .insert_as_lsb(u8::texel(), val, bits);
264            before ^ *val
265        }
266
267        let mut val = 0b1000_1010u8;
268        assert_eq!(insert_simple(0..1, 1, &mut val), 0);
269        assert_eq!(insert_simple(1..2, 0, &mut val), 0);
270        assert_eq!(insert_simple(2..3, 0, &mut val), 0);
271        assert_eq!(insert_simple(6..7, 1, &mut val), 0);
272        assert_eq!(insert_simple(0..7, val as u32 >> 1, &mut val), 0);
273        assert_eq!(insert_simple(1..8, (val & 0x7f) as u32, &mut val), 0);
274
275        assert_eq!(insert_simple(0..0, 0, &mut val), 0);
276        assert_eq!(insert_simple(1..1, 0, &mut val), 0);
277    }
278}