image/utils/
mod.rs

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