1use crate::packed::unpack_row;
16use flashraw_core::{Error, PackOrder, Result, par};
17
18#[derive(Debug, Clone, Copy)]
20pub struct PackedParams {
21 pub width: usize,
22 pub height: usize,
23 pub bits: u32,
24 pub flags: u32,
26}
27
28impl PackedParams {
29 pub fn row_bytes(&self) -> usize {
31 let mut b = self.width * self.bits as usize / 8;
32 if self.flags & 128 != 0 {
33 b += b & 1;
34 }
35 b
36 }
37 fn word_bits(&self) -> u32 {
38 8 + (self.flags & 24)
39 }
40}
41
42pub fn packed_rows(file: &[u8], offset: usize, p: PackedParams, out: &mut [u16], parallel: bool) -> Result<()> {
46 let (w, h) = (p.width, p.height);
47 if !(1..=16).contains(&p.bits) || w == 0 || out.len() < w * h {
48 return Err(Error::corrupt("invalid packed raster geometry"));
49 }
50 let bwide = p.row_bytes();
51 let word = p.word_bits() as usize;
52 let fast_order = match word {
53 8 => Some(PackOrder::MsbBytes),
54 16 => Some(PackOrder::Msb16),
55 32 => Some(PackOrder::Msb32),
56 _ => None,
57 };
58 if let (Some(order), true) = (fast_order, p.flags & 3 == 0 && (bwide * 8) % word == 0) {
59 let src = file.get(offset..).unwrap_or(&[]);
60 let swap = p.flags & 64 != 0;
61 par::for_each_row_chunk(&mut out[..w * h], w, 16, parallel, |first, chunk| {
62 for (i, row) in chunk.chunks_exact_mut(w).enumerate() {
63 let y = first + i;
64 let Some(s) = src.get(y * bwide..) else { break };
65 let s = &s[..bwide.min(s.len())];
66 if s.len() * 8 < w * p.bits as usize {
67 let mut tmp = s.to_vec();
69 tmp.resize(bwide + 8, 0);
70 unpack_row(&tmp, row, p.bits, order, false);
71 } else {
72 unpack_row(s, row, p.bits, order, true);
73 }
74 if swap {
75 for pair in row.chunks_exact_mut(2) {
76 pair.swap(0, 1);
77 }
78 }
79 }
80 });
81 return Ok(());
82 }
83 packed_rows_reference(file, offset, p, out);
84 Ok(())
85}
86
87pub fn packed_rows_reference(file: &[u8], offset: usize, p: PackedParams, out: &mut [u16]) {
90 let (w, h, bits) = (p.width, p.height, p.bits as i64);
91 let bwide = p.row_bytes();
92 let rbits = bwide as i64 * 8 - w as i64 * bits;
93 let bite = p.word_bits() as i64;
94 let half = h.div_ceil(2);
95 let mut pos = offset;
96 let mut bitbuf: u64 = 0;
97 let mut vbits: i64 = 0;
98 let byte = |pos: &mut usize| -> u64 {
99 let b = file.get(*pos).copied().unwrap_or(0);
100 *pos += 1;
101 b as u64
102 };
103 let swap = (p.flags >> 6 & 1) as usize;
104 for irow in 0..h {
105 let mut row = irow;
106 if p.flags & 2 != 0 {
107 row = irow % half * 2 + irow / half;
108 if row == 1 && p.flags & 4 != 0 {
109 vbits = 0;
110 pos = file.len() >> 3 << 2;
111 }
112 }
113 if pos > file.len() {
114 break;
115 }
116 for col in 0..w {
117 vbits -= bits;
118 while vbits < 0 {
119 bitbuf <<= bite as u32;
120 for i in (0..bite).step_by(8) {
121 bitbuf |= byte(&mut pos) << i;
122 }
123 vbits += bite;
124 }
125 let val = ((bitbuf << (64 - bits - vbits) as u32) >> (64 - bits) as u32) & 0x7fff_ffff;
127 if let Some(o) = out.get_mut(row * w + (col ^ swap)).filter(|_| (col ^ swap) < w) {
128 *o = val as u16;
129 }
130 if p.flags & 1 != 0 && col % 10 == 9 {
131 pos += 1;
132 }
133 }
134 vbits -= rbits;
135 }
136}
137
138pub fn mipi10_rows(src: &[u8], width: usize, height: usize, stride: usize, out: &mut [u16], parallel: bool) {
142 let need = width.div_ceil(4) * 5;
143 par::for_each_row_chunk(&mut out[..width * height], width, 32, parallel, |first, chunk| {
144 let mut tmp = vec![0u8; need + 8];
145 for (i, row) in chunk.chunks_exact_mut(width).enumerate() {
146 let y = first + i;
147 let Some(s) = src.get(y * stride..) else { break };
148 let n = need.min(s.len());
149 tmp[..n].copy_from_slice(&s[..n]);
150 tmp[n..].fill(0);
151 for (g, px) in row.chunks_mut(4).enumerate() {
152 let d = &tmp[g * 5..g * 5 + 5];
153 for (c, v) in px.iter_mut().enumerate() {
154 *v = ((d[c] as u16) << 2) | ((d[4] >> (c * 2)) & 3) as u16;
155 }
156 }
157 }
158 });
159}
160
161pub fn loose10_rows(src: &[u8], width: usize, height: usize, out: &mut [u16], parallel: bool) {
164 let bwide = width.div_ceil(6) * 8;
165 par::for_each_row_chunk(&mut out[..width * height], width, 32, parallel, |first, chunk| {
166 for (i, row) in chunk.chunks_exact_mut(width).enumerate() {
167 let y = first + i;
168 let Some(s) = src.get(y * bwide..) else { break };
169 for (g, px) in row.chunks_mut(6).enumerate() {
170 let mut b = [0u8; 8];
171 if let Some(word) = s.get(g * 8..g * 8 + 8) {
172 b.copy_from_slice(word);
173 }
174 let v = u64::from_le_bytes(b);
175 for (c, o) in px.iter_mut().enumerate() {
176 *o = (v >> (c * 10) & 0x3ff) as u16;
177 }
178 }
179 }
180 });
181}
182
183#[allow(clippy::too_many_arguments)]
186pub fn u16_rows(
187 src: &[u8],
188 width: usize,
189 height: usize,
190 big_endian: bool,
191 shift: u32,
192 bottom_up: bool,
193 out: &mut [u16],
194 parallel: bool,
195) {
196 let rb = width * 2;
197 par::for_each_row_chunk(&mut out[..width * height], width, 32, parallel, |first, chunk| {
198 for (i, row) in chunk.chunks_exact_mut(width).enumerate() {
199 let y = first + i;
200 let sy = if bottom_up { height - 1 - y } else { y };
201 let Some(s) = src.get(sy * rb..) else { continue };
202 for (o, b) in row.iter_mut().zip(s.chunks_exact(2)) {
203 let v = if big_endian { u16::from_be_bytes([b[0], b[1]]) } else { u16::from_le_bytes([b[0], b[1]]) };
204 *o = v >> shift.min(15);
205 }
206 }
207 });
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 fn lcg(n: usize, seed: u32) -> Vec<u8> {
215 let mut s = seed;
216 (0..n)
217 .map(|_| {
218 s = s.wrapping_mul(1664525).wrapping_add(1013904223);
219 (s >> 24) as u8
220 })
221 .collect()
222 }
223
224 #[test]
225 fn fast_path_matches_reference() {
226 for &(w, h, bits, flags) in &[
227 (3152usize, 5usize, 10u32, 40u32 | 128),
228 (2346, 4, 12, 128),
229 (4320, 3, 10, 224),
230 (100, 6, 12, 24 | 128),
231 (64, 4, 16, 0),
232 ] {
233 let p = PackedParams { width: w, height: h, bits, flags };
234 let file = lcg(16 + p.row_bytes() * h + 16, w as u32);
235 let mut a = vec![0u16; w * h];
236 let mut b = vec![0u16; w * h];
237 packed_rows(&file, 16, p, &mut a, true).unwrap();
238 packed_rows_reference(&file, 16, p, &mut b);
239 assert_eq!(a, b, "{w}x{h} {bits} bits flags {flags}");
240 }
241 }
242
243 #[test]
244 fn reference_handles_odd_flags_without_panicking() {
245 for flags in [1u32, 2, 6, 7, 9, 16, 65, 255] {
246 let p = PackedParams { width: 31, height: 7, bits: 12, flags };
247 let file = lcg(400, flags);
248 let mut out = vec![0u16; 31 * 7];
249 packed_rows(&file, 3, p, &mut out, false).unwrap();
250 packed_rows(&file[..20], 3, p, &mut out, false).unwrap();
252 }
253 }
254
255 #[test]
256 fn mipi_and_loose() {
257 let src = [0x12u8, 0x34, 0x56, 0x78, 0b11_10_01_00];
258 let mut out = vec![0u16; 4];
259 mipi10_rows(&src, 4, 1, 5, &mut out, false);
260 assert_eq!(out, vec![0x12 << 2, (0x34 << 2) | 1, (0x56 << 2) | 2, (0x78 << 2) | 3]);
261 let v: u64 = (1..=6).fold(0, |acc, i| acc | ((i as u64 * 100) << ((i - 1) * 10)));
262 let mut out = vec![0u16; 6];
263 loose10_rows(&v.to_le_bytes(), 6, 1, &mut out, false);
264 assert_eq!(out, vec![100, 200, 300, 400, 500, 600]);
265 }
266
267 #[test]
268 fn u16_variants() {
269 let src = [0x01u8, 0x02, 0x03, 0x04];
270 let mut out = vec![0u16; 2];
271 u16_rows(&src, 2, 1, false, 0, false, &mut out, false);
272 assert_eq!(out, vec![0x0201, 0x0403]);
273 u16_rows(&src, 2, 1, true, 4, false, &mut out, false);
274 assert_eq!(out, vec![0x0102 >> 4, 0x0304 >> 4]);
275 let mut out = vec![0u16; 2];
276 u16_rows(&src, 1, 2, false, 0, true, &mut out, false);
277 assert_eq!(out, vec![0x0403, 0x0201]);
278 }
279}