1 | //! Utilities |
2 | |
3 | use std::iter::repeat; |
4 | |
5 | #[inline (always)] |
6 | pub(crate) fn expand_packed<F>(buf: &mut [u8], channels: usize, bit_depth: u8, mut func: F) |
7 | where |
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 |
37 | pub(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 |
68 | pub(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 |
74 | pub(crate) fn vec_copy_to_u8<T>(vec: &[T]) -> Vec<u8> |
75 | where |
76 | T: bytemuck::Pod, |
77 | { |
78 | bytemuck::cast_slice(vec).to_owned() |
79 | } |
80 | |
81 | #[inline ] |
82 | pub(crate) fn clamp<N>(a: N, min: N, max: N) -> N |
83 | where |
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)] |
96 | mod 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 | |