image/utils/
mod.rs

1//!  Utilities
2
3use std::iter::repeat;
4
5#[inline(always)]
6pub(crate) fn expand_packed<F>(buf: &mut [u8], channels: usize, bit_depth: u8, mut func: F)
7where
8    F: FnMut(u8, &mut [u8]),
9{
10    let pixels = buf.len() / channels * bit_depth as usize;
11    let extra = pixels % 8;
12    let entries = pixels / 8
13        + match extra {
14            0 => 0,
15            _ => 1,
16        };
17    let mask = ((1u16 << bit_depth) - 1) as u8;
18    let i = (0..entries)
19        .rev() // Reverse iterator
20        .flat_map(|idx|
21            // This has to be reversed to
22            (0..8/bit_depth).map(|i| i*bit_depth).zip(repeat(idx)))
23        .skip(extra);
24    let buf_len = buf.len();
25    let j_inv = (channels..buf_len).step_by(channels);
26    for ((shift, i), j_inv) in i.zip(j_inv) {
27        let j = buf_len - j_inv;
28        let pixel = (buf[i] & (mask << shift)) >> shift;
29        func(pixel, &mut buf[j..(j + channels)]);
30    }
31}
32
33/// Expand a buffer of packed 1, 2, or 4 bits integers into u8's. Assumes that
34/// every `row_size` entries there are padding bits up to the next byte boundary.
35#[allow(dead_code)]
36// When no image formats that use it are enabled
37pub(crate) fn expand_bits(bit_depth: u8, row_size: u32, buf: &[u8]) -> Vec<u8> {
38    // Note: this conversion assumes that the scanlines begin on byte boundaries
39    let mask = (1u8 << bit_depth as usize) - 1;
40    let scaling_factor = 255 / ((1 << bit_depth as usize) - 1);
41    let bit_width = row_size * u32::from(bit_depth);
42    let skip = if bit_width % 8 == 0 {
43        0
44    } else {
45        (8 - bit_width % 8) / u32::from(bit_depth)
46    };
47    let row_len = row_size + skip;
48    let mut p = Vec::new();
49    let mut i = 0;
50    for v in buf {
51        for shift_inv in 1..=8 / bit_depth {
52            let shift = 8 - bit_depth * shift_inv;
53            // skip the pixels that can be neglected because scanlines should
54            // start at byte boundaries
55            if i % (row_len as usize) < (row_size as usize) {
56                let pixel = (v & (mask << shift as usize)) >> shift as usize;
57                p.push(pixel * scaling_factor);
58            }
59            i += 1;
60        }
61    }
62    p
63}
64
65/// Checks if the provided dimensions would cause an overflow.
66#[allow(dead_code)]
67// When no image formats that use it are enabled
68pub(crate) fn check_dimension_overflow(width: u32, height: u32, bytes_per_pixel: u8) -> bool {
69    u64::from(width) * u64::from(height) > u64::MAX / u64::from(bytes_per_pixel)
70}
71
72#[allow(dead_code)]
73// When no image formats that use it are enabled
74pub(crate) fn vec_copy_to_u8<T>(vec: &[T]) -> Vec<u8>
75where
76    T: bytemuck::Pod,
77{
78    bytemuck::cast_slice(vec).to_owned()
79}
80
81#[inline]
82pub(crate) fn clamp<N>(a: N, min: N, max: N) -> N
83where
84    N: PartialOrd,
85{
86    if a < min {
87        min
88    } else if a > max {
89        max
90    } else {
91        a
92    }
93}
94
95#[cfg(test)]
96mod test {
97    #[test]
98    fn gray_to_luma8_skip() {
99        let check = |bit_depth, w, from, to| {
100            assert_eq!(super::expand_bits(bit_depth, w, from), to);
101        };
102        // Bit depth 1, skip is more than half a byte
103        check(
104            1,
105            10,
106            &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
107            vec![
108                255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
109            ],
110        );
111        // Bit depth 2, skip is more than half a byte
112        check(
113            2,
114            5,
115            &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
116            vec![255, 255, 0, 0, 255, 0, 0, 255, 255, 255],
117        );
118        // Bit depth 2, skip is 0
119        check(
120            2,
121            4,
122            &[0b11110000, 0b00001111],
123            vec![255, 255, 0, 0, 0, 0, 255, 255],
124        );
125        // Bit depth 4, skip is half a byte
126        check(4, 1, &[0b11110011, 0b00001100], vec![255, 0]);
127    }
128}