Coverage Report

Created: 2026-01-09 07:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/utils/mod.rs
Line
Count
Source
1
//!  Utilities
2
3
use std::collections::TryReserveError;
4
use std::iter::repeat;
5
6
#[inline(always)]
7
0
pub(crate) fn expand_packed<F>(buf: &mut [u8], channels: usize, bit_depth: u8, mut func: F)
8
0
where
9
0
    F: FnMut(u8, &mut [u8]),
10
{
11
0
    let pixels = buf.len() / channels * bit_depth as usize;
12
0
    let extra = pixels % 8;
13
0
    let entries = pixels / 8
14
0
        + match extra {
15
0
            0 => 0,
16
0
            _ => 1,
17
        };
18
0
    let mask = ((1u16 << bit_depth) - 1) as u8;
19
0
    let i = (0..entries)
20
0
        .rev() // Reverse iterator
21
0
        .flat_map(|idx|
22
            // This has to be reversed to
23
0
            (0..8/bit_depth).map(|i| i*bit_depth).zip(repeat(idx)))
24
0
        .skip(extra);
25
0
    let buf_len = buf.len();
26
0
    let j_inv = (channels..buf_len).step_by(channels);
27
0
    for ((shift, i), j_inv) in i.zip(j_inv) {
28
0
        let j = buf_len - j_inv;
29
0
        let pixel = (buf[i] & (mask << shift)) >> shift;
30
0
        func(pixel, &mut buf[j..(j + channels)]);
31
0
    }
32
0
}
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
38
273k
pub(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
273k
    let mask = (1u8 << bit_depth as usize) - 1;
41
273k
    let scaling_factor = 255 / ((1 << bit_depth as usize) - 1);
42
273k
    let bit_width = row_size * u32::from(bit_depth);
43
273k
    let skip = if bit_width % 8 == 0 {
44
321
        0
45
    } else {
46
272k
        (8 - bit_width % 8) / u32::from(bit_depth)
47
    };
48
273k
    let row_len = row_size + skip;
49
273k
    let mut p = Vec::new();
50
273k
    let mut i = 0;
51
4.08M
    for v in buf {
52
30.5M
        for shift_inv in 1..=8 / bit_depth {
53
30.5M
            let shift = 8 - bit_depth * shift_inv;
54
            // skip the pixels that can be neglected because scanlines should
55
            // start at byte boundaries
56
30.5M
            if i % (row_len as usize) < (row_size as usize) {
57
28.9M
                let pixel = (v & (mask << shift as usize)) >> shift as usize;
58
28.9M
                p.push(pixel * scaling_factor);
59
28.9M
            }
60
30.5M
            i += 1;
61
        }
62
    }
63
273k
    p
64
273k
}
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
69
3.26k
pub(crate) fn check_dimension_overflow(width: u32, height: u32, bytes_per_pixel: u8) -> bool {
70
3.26k
    u64::from(width) * u64::from(height) > u64::MAX / u64::from(bytes_per_pixel)
71
3.26k
}
72
73
#[allow(dead_code)]
74
// When no image formats that use it are enabled
75
0
pub(crate) fn vec_copy_to_u8<T>(vec: &[T]) -> Vec<u8>
76
0
where
77
0
    T: bytemuck::Pod,
78
{
79
0
    bytemuck::cast_slice(vec).to_owned()
80
0
}
81
82
#[inline]
83
0
pub(crate) fn clamp<N>(a: N, min: N, max: N) -> N
84
0
where
85
0
    N: PartialOrd,
86
{
87
0
    if a < min {
88
0
        min
89
0
    } else if a > max {
90
0
        max
91
    } else {
92
0
        a
93
    }
94
0
}
Unexecuted instantiation: image::utils::clamp::<f64>
Unexecuted instantiation: image::utils::clamp::<f32>
Unexecuted instantiation: image::utils::clamp::<i32>
Unexecuted instantiation: image::utils::clamp::<u32>
95
96
#[inline]
97
2.52k
pub(crate) fn vec_try_with_capacity<T>(capacity: usize) -> Result<Vec<T>, TryReserveError> {
98
2.52k
    let mut vec = Vec::new();
99
2.52k
    vec.try_reserve_exact(capacity)?;
100
2.52k
    Ok(vec)
101
2.52k
}
Unexecuted instantiation: image::utils::vec_try_with_capacity::<f32>
image::utils::vec_try_with_capacity::<u8>
Line
Count
Source
97
2.52k
pub(crate) fn vec_try_with_capacity<T>(capacity: usize) -> Result<Vec<T>, TryReserveError> {
98
2.52k
    let mut vec = Vec::new();
99
2.52k
    vec.try_reserve_exact(capacity)?;
100
2.52k
    Ok(vec)
101
2.52k
}
Unexecuted instantiation: image::utils::vec_try_with_capacity::<u16>
102
103
/// Returns whether `T` can only represent integer values.
104
#[inline]
105
0
pub(crate) fn is_integer<T: num_traits::NumCast + num_traits::Zero>() -> bool {
106
    // This uses a cast of 0.5 to T, because integers will truncate to zero
107
    // while types that can represent fractional values will return something
108
    // other than zero.
109
0
    <T as num_traits::NumCast>::from(0.5).unwrap().is_zero()
110
0
}
Unexecuted instantiation: image::utils::is_integer::<f64>
Unexecuted instantiation: image::utils::is_integer::<f32>
Unexecuted instantiation: image::utils::is_integer::<u8>
Unexecuted instantiation: image::utils::is_integer::<u32>
Unexecuted instantiation: image::utils::is_integer::<u16>
111
112
#[cfg(test)]
113
mod test {
114
    #[test]
115
    fn gray_to_luma8_skip() {
116
        let check = |bit_depth, w, from, to| {
117
            assert_eq!(super::expand_bits(bit_depth, w, from), to);
118
        };
119
        // Bit depth 1, skip is more than half a byte
120
        check(
121
            1,
122
            10,
123
            &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
124
            vec![
125
                255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
126
            ],
127
        );
128
        // Bit depth 2, skip is more than half a byte
129
        check(
130
            2,
131
            5,
132
            &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
133
            vec![255, 255, 0, 0, 255, 0, 0, 255, 255, 255],
134
        );
135
        // Bit depth 2, skip is 0
136
        check(
137
            2,
138
            4,
139
            &[0b11110000, 0b00001111],
140
            vec![255, 255, 0, 0, 0, 0, 255, 255],
141
        );
142
        // Bit depth 4, skip is half a byte
143
        check(4, 1, &[0b11110011, 0b00001100], vec![255, 0]);
144
    }
145
}