/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 | | } |