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: usize = buf.len() / channels * bit_depth as usize;
11 let extra: usize = pixels % 8;
12 let entries: usize = pixels / 8
13 + match extra {
14 0 => 0,
15 _ => 1,
16 };
17 let mask: u8 = ((1u16 << bit_depth) - 1) as u8;
18 let i: impl Iterator = (0..entries)
19 .rev() // Reverse iterator
20 .flat_map(|idx: usize|
21 // This has to be reversed to
22 (0..8/bit_depth).map(|i: u8| i*bit_depth).zip(repeat(elt:idx)))
23 .skip(extra);
24 let buf_len: usize = buf.len();
25 let j_inv: impl Iterator = (channels..buf_len).step_by(step:channels);
26 for ((shift: u8, i: usize), j_inv: usize) in i.zip(j_inv) {
27 let j: usize = buf_len - j_inv;
28 let pixel: u8 = (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 width as u64 * height as u64 > std::u64::MAX / bytes_per_pixel as u64
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}
129