1use crate::packed::{self, PackedLayout};
20use flashraw_core::{Error, PackOrder, Result};
21
22#[derive(Debug, Clone, Copy)]
24pub struct PackedSpec {
25 pub width: usize,
26 pub height: usize,
27 pub bits: u32,
29 pub flags: u32,
31 pub field2_offset: Option<usize>,
33}
34
35impl PackedSpec {
36 fn bwide(&self) -> usize {
38 let b = self.width * self.bits as usize / 8;
39 b + (b & ((self.flags as usize >> 7) & 1))
40 }
41
42 fn rbits(&self) -> i64 {
44 self.bwide() as i64 * 8 - (self.width * self.bits as usize) as i64
45 }
46
47 pub fn row_bytes(&self) -> usize {
49 let b = self.bwide();
50 if self.flags & 1 != 0 { b * 16 / 15 } else { b }
51 }
52
53 fn bite(&self) -> u32 {
55 8 + (self.flags & 24)
56 }
57
58 pub fn fast_layout(&self) -> Option<PackedLayout> {
60 if self.flags & 7 != 0 || self.rbits() < 0 {
61 return None;
62 }
63 let rbits = self.rbits() as usize;
64 let swap = self.flags & 64 != 0;
65 let order = match (self.bite(), swap) {
66 (8, false) => PackOrder::MsbBytes,
67 (24, true) if self.bits == 12 => PackOrder::LsbBytes,
69 (16, false) if self.bits == 16 || (self.width * self.bits as usize) % 16 == 0 => PackOrder::Msb16,
70 (32, false) if (self.width * self.bits as usize) % 32 == 0 => PackOrder::Msb32,
71 _ => return None,
72 };
73 if rbits % 8 != 0 || (order != PackOrder::MsbBytes && order != PackOrder::LsbBytes && rbits != 0) {
74 return None;
75 }
76 Some(PackedLayout {
77 width: self.width,
78 height: self.height,
79 bits: self.bits,
80 order,
81 row_stride: self.row_bytes(),
82 })
83 }
84
85 pub fn required_bytes(&self) -> usize {
87 if self.flags & 4 != 0 {
88 let half = self.height.div_ceil(2);
89 self.field2_offset.unwrap_or(0) + (self.height - half) * self.row_bytes()
90 } else {
91 self.height * self.row_bytes()
92 }
93 }
94}
95
96pub fn decode(
101 src: &[u8],
102 spec: &PackedSpec,
103 out: &mut [u16],
104 stride: usize,
105 parallel: bool,
106 simd: bool,
107 lenient: bool,
108) -> Result<()> {
109 if spec.bits == 0 || spec.bits > 16 || spec.width == 0 {
110 return Err(Error::corrupt(format!("unsupported packed layout ({} bits)", spec.bits)));
111 }
112 if stride < spec.width || out.len() < stride * spec.height {
113 return Err(Error::InvalidArgument("packed: output too small".into()));
114 }
115 if let Some(layout) = spec.fast_layout() {
116 return packed::unpack(src, layout, out, stride, parallel, simd, lenient);
117 }
118 decode_exact_with(src, spec, out, stride, lenient)
119}
120
121pub fn decode_exact(src: &[u8], spec: &PackedSpec, out: &mut [u16], stride: usize) -> Result<()> {
123 decode_exact_with(src, spec, out, stride, false)
124}
125
126fn decode_exact_with(src: &[u8], spec: &PackedSpec, out: &mut [u16], stride: usize, lenient: bool) -> Result<()> {
129 let (w, h, bits) = (spec.width, spec.height, spec.bits as i32);
130 let bite = spec.bite() as i32;
131 let rbits = spec.rbits();
132 let half = h.div_ceil(2);
133 let swap = ((spec.flags >> 6) & 1) as usize;
134 let mut pos = 0usize;
135 let mut bitbuf: u64 = 0;
136 let mut vbits: i64 = 0;
137 let mut eof = false;
138 for irow in 0..h {
139 let mut row = irow;
140 if spec.flags & 2 != 0 {
141 row = irow % half * 2 + irow / half;
142 if row == 1 && spec.flags & 4 != 0 {
143 vbits = 0;
144 pos = spec.field2_offset.ok_or_else(|| Error::corrupt("missing second field offset"))?;
145 }
146 }
147 if eof {
148 return Err(Error::corrupt(format!("truncated packed data at row {irow}/{h}")));
149 }
150 let line =
151 out.get_mut(row * stride..row * stride + w).ok_or_else(|| Error::corrupt("packed row out of range"))?;
152 for col in 0..w {
153 vbits -= bits as i64;
154 while vbits < 0 {
155 bitbuf = if bite >= 64 { 0 } else { bitbuf << bite };
156 for i in (0..bite).step_by(8) {
157 let b = match src.get(pos) {
158 Some(&b) => b as u64,
159 None => {
160 eof = true;
161 0
162 }
163 };
164 pos += 1;
165 bitbuf |= b << i;
166 }
167 vbits += bite as i64;
168 }
169 if eof && lenient {
170 for c in col..w {
171 if let Some(v) = line.get_mut(c ^ swap) {
172 *v = 0;
173 }
174 }
175 return Ok(());
176 }
177 let val = ((bitbuf << (64 - bits as i64 - vbits)) >> (64 - bits)) as u16;
178 line[col ^ swap] = val;
179 if spec.flags & 1 != 0 && col % 10 == 9 {
180 pos += 1;
181 }
182 }
183 vbits -= rbits;
184 }
185 Ok(())
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn pseudo(n: usize, seed: u64) -> Vec<u8> {
193 let mut x = seed | 1;
194 (0..n)
195 .map(|_| {
196 x ^= x << 13;
197 x ^= x >> 7;
198 x ^= x << 17;
199 x as u8
200 })
201 .collect()
202 }
203
204 #[test]
205 fn fast_layouts_match_exact_emulation() {
206 for (bits, flags, width, fast) in [
207 (12u32, 0u32, 40usize, true),
208 (12, 80, 40, true),
209 (12, 24, 40, true),
210 (14, 0, 64, true),
211 (12, 128, 42, true),
212 (12, 128, 41, false),
213 (16, 0, 10, true),
214 (12, 16, 40, false),
215 (12, 0, 41, false),
216 ] {
217 let spec = PackedSpec { width, height: 7, bits, flags, field2_offset: None };
218 let src = pseudo(spec.required_bytes() + 8, bits as u64 * 31 + flags as u64);
219 let mut a = vec![0u16; width * 7];
220 let mut b = vec![0u16; width * 7];
221 decode_exact(&src, &spec, &mut a, width).unwrap();
222 assert_eq!(spec.fast_layout().is_some(), fast, "{bits} {flags} {width}");
223 decode(&src, &spec, &mut b, width, false, true, false).unwrap();
224 assert_eq!(a, b, "bits {bits} flags {flags}");
225 }
226 }
227
228 #[test]
229 fn lsb24_swap_is_lsb_packing() {
230 let src = [0xBC, 0x3A, 0x12];
232 let spec = PackedSpec { width: 2, height: 1, bits: 12, flags: 80, field2_offset: None };
233 let mut out = [0u16; 2];
234 decode_exact(&src, &spec, &mut out, 2).unwrap();
235 assert_eq!(out, [0xABC, 0x123]);
236 }
237
238 #[test]
239 fn padding_every_ten_samples() {
240 let mut src = vec![0u8; 32];
242 src[0] = 0xFF;
243 src[1] = 0xF0; src[15] = 0xAA; src[16] = 0x80; let spec = PackedSpec { width: 20, height: 1, bits: 12, flags: 1, field2_offset: None };
247 let mut out = vec![0u16; 20];
248 decode(&src, &spec, &mut out, 20, false, false, false).unwrap();
249 assert_eq!(out[0], 0xFFF);
250 assert_eq!(out[10], 0x800);
251 assert_eq!(spec.row_bytes(), 32);
252 }
253
254 #[test]
255 fn interlaced_fields() {
256 let spec = PackedSpec { width: 2, height: 4, bits: 8, flags: 2, field2_offset: None };
257 let src = [0u8, 1, 2, 3, 4, 5, 6, 7];
258 let mut out = vec![0u16; 8];
259 decode(&src, &spec, &mut out, 2, false, false, false).unwrap();
260 assert_eq!(out, vec![0, 1, 4, 5, 2, 3, 6, 7]);
262 }
263
264 #[test]
265 fn truncation_is_an_error() {
266 let spec = PackedSpec { width: 16, height: 8, bits: 12, flags: 1, field2_offset: None };
267 let src = vec![0u8; 40];
268 let mut out = vec![0u16; 16 * 8];
269 assert!(decode(&src, &spec, &mut out, 16, false, false, false).is_err());
270 let spec = PackedSpec { width: 16, height: 8, bits: 12, flags: 0, field2_offset: None };
271 assert!(decode(&src, &spec, &mut out, 16, false, false, false).is_err());
272 }
273
274 #[test]
275 fn lenient_keeps_complete_samples() {
276 let spec = PackedSpec { width: 16, height: 4, bits: 12, flags: 1, field2_offset: None };
279 let full = pseudo(spec.required_bytes(), 7);
280 let mut a = vec![0u16; 16 * 4];
281 decode(&full, &spec, &mut a, 16, false, false, false).unwrap();
282 let mut b = vec![0u16; 16 * 4];
283 decode(&full[..40], &spec, &mut b, 16, false, false, true).unwrap();
284 assert_eq!(&b[..16], &a[..16]);
285 let kept = b[16..32].iter().take_while(|&&v| v != 0).count();
286 assert!(kept > 0 && kept < 16);
287 assert_eq!(&b[16..16 + kept], &a[16..16 + kept]);
288 assert!(b[16 + kept..].iter().all(|&v| v == 0));
289 }
290}