1use flashraw_core::bits::{BitPumpMsb32, BitRead};
17use flashraw_core::huffman::HuffTable;
18use flashraw_core::{Error, ReadAt, Result};
19
20#[derive(Debug, Clone)]
22pub struct HasselbladHeader {
23 pub table: HuffTable,
24 pub predictor: u32,
26 pub width: u32,
28 pub height: u32,
29 pub scan_offset: usize,
31}
32
33impl HasselbladHeader {
34 pub fn parse(data: &[u8]) -> Result<Self> {
36 if data.len() < 4 || data[0] != 0xFF || data[1] != 0xD8 {
37 return Err(Error::corrupt("Hasselblad: missing SOI"));
38 }
39 let mut pos = 2usize;
40 let mut table: Option<HuffTable> = None;
41 let (mut width, mut height) = (0u32, 0u32);
42 loop {
43 while pos < data.len() && data[pos] != 0xFF {
44 pos += 1;
45 }
46 while pos < data.len() && data[pos] == 0xFF {
47 pos += 1;
48 }
49 let marker = *data.get(pos).ok_or_else(|| Error::corrupt("Hasselblad: truncated header"))?;
50 pos += 1;
51 if marker == 0xD8 || (0xD0..=0xD7).contains(&marker) {
52 continue;
53 }
54 if marker == 0xD9 {
55 return Err(Error::corrupt("Hasselblad: EOI before SOS"));
56 }
57 let len = data.u16be(pos)? as usize;
58 if len < 2 {
59 return Err(Error::corrupt("Hasselblad: invalid segment length"));
60 }
61 let seg = flashraw_core::bytes::slice_at(data, pos + 2, len - 2)?;
62 match marker {
63 0xC3 if seg.len() >= 5 => {
64 height = seg.u16be(1)? as u32;
65 width = seg.u16be(3)? as u32;
66 }
67 0xC4 => {
68 let mut p = 0usize;
69 while p + 17 <= seg.len() {
70 let id = seg[p] & 0x0F;
71 let counts = &seg[p + 1..p + 17];
72 let total: usize = counts.iter().map(|&c| c as usize).sum();
73 let vals = flashraw_core::bytes::slice_at(seg, p + 17, total)?;
74 if id == 0 || table.is_none() {
75 table = Some(HuffTable::from_counts(counts, vals, false)?);
76 }
77 p += 17 + total;
78 }
79 }
80 0xDA => {
81 let ns = *seg.first().ok_or_else(|| Error::corrupt("Hasselblad: short SOS"))? as usize;
82 let predictor = *seg.get(1 + 2 * ns).ok_or_else(|| Error::corrupt("Hasselblad: short SOS"))? as u32;
83 let table = table.ok_or_else(|| Error::corrupt("Hasselblad: missing Huffman table"))?;
84 return Ok(HasselbladHeader { table, predictor, width, height, scan_offset: pos + len });
85 }
86 _ => {}
87 }
88 pos += len;
89 }
90 }
91}
92
93#[inline(always)]
96fn diff<B: BitRead>(bits: &mut B, len: u32) -> i32 {
97 if len == 0 {
98 return 0;
99 }
100 let mut d = bits.get(len) as i32;
101 if d & (1 << (len - 1)) == 0 {
102 d -= (1 << len) - 1;
103 }
104 if d == 65535 { -32768 } else { d }
105}
106
107pub fn decode(
110 data: &[u8],
111 hdr: &HasselbladHeader,
112 out: &mut [u16],
113 width: usize,
114 height: usize,
115 base: i32,
116) -> Result<()> {
117 if width % 2 != 0 {
118 return Err(Error::unsupported_compression("Hasselblad", "odd raster width"));
119 }
120 if out.len() < width * height {
121 return Err(Error::InvalidArgument("Hasselblad: output buffer too small".into()));
122 }
123 let scan = data.get(hdr.scan_offset..).ok_or_else(|| Error::corrupt("Hasselblad: scan beyond data"))?;
124 let mut bits = BitPumpMsb32::new(scan);
125 let t = &hdr.table;
126 let grad = hdr.predictor == 11;
127 if !grad {
128 return decode_left(&mut bits, t, out, width, height, 0x8000 + base);
129 }
130 let mut rows: [Vec<i32>; 3] = [vec![0; width], vec![0; width], vec![0; width]];
132 let start = 0x8000 + base;
133 for row in 0..height {
134 rows.rotate_left(1); let (prev2, rest) = rows.split_at_mut(1);
136 let up2 = &prev2[0];
137 let cur = &mut rest[1];
138 let dst = &mut out[row * width..(row + 1) * width];
139 for col in (0..width).step_by(2) {
140 let l0 = t.decode_symbol(&mut bits)?;
141 let l1 = t.decode_symbol(&mut bits)?;
142 if l0 > 16 || l1 > 16 {
143 return Err(Error::corrupt("Hasselblad: difference length > 16"));
144 }
145 let d = [diff(&mut bits, l0), diff(&mut bits, l1)];
146 for (k, &dk) in d.iter().enumerate() {
147 let s = col + k;
148 let mut pred = if col > 0 { cur[s - 2] } else { start };
149 if grad && col > 0 && row > 1 {
150 pred += up2[s] / 2 - up2[s - 2] / 2;
151 }
152 pred = pred.wrapping_add(dk);
153 dst[s] = pred as u16;
154 cur[s] = pred;
155 }
156 }
157 if bits.exhausted() {
158 return Err(Error::corrupt(format!("Hasselblad: data ends at row {row}/{height}")));
159 }
160 }
161 Ok(())
162}
163
164pub fn decode_par(
168 data: &[u8],
169 hdr: &HasselbladHeader,
170 out: &mut [u16],
171 width: usize,
172 height: usize,
173 base: i32,
174 parallel: bool,
175) -> Result<()> {
176 if parallel
177 && hdr.predictor != 11
178 && width % 2 == 0
179 && width > 0
180 && out.len() >= width * height
181 && width * height >= crate::ljpeg_par::PARALLEL_MIN_SAMPLES
182 && decode_parallel(data, hdr, &mut out[..width * height], width, base, None)
183 {
184 return Ok(());
185 }
186 decode(data, hdr, out, width, height, base)
187}
188
189struct PairScan<'a> {
191 scan: &'a [u8],
192 table: &'a HuffTable,
193}
194
195impl crate::par_entropy::SymbolStream for PairScan<'_> {
196 type Pump<'p>
197 = BitPumpMsb32<'p>
198 where
199 Self: 'p;
200 const WIDTH: usize = 2;
201 fn cycle(&self) -> usize {
202 1
203 }
204 fn period(&self) -> usize {
205 1
206 }
207 fn total_bits(&self) -> u64 {
208 self.scan.len() as u64 * 8
209 }
210 fn end_check(&self, pos: u64) -> crate::par_entropy::EndCheck {
211 use crate::par_entropy::EndCheck;
212 let end = self.total_bits();
216 if pos <= end {
217 EndCheck::Valid
218 } else if pos <= end + 64 {
219 EndCheck::Unknown
220 } else {
221 EndCheck::Invalid
222 }
223 }
224 fn pump_at(&self, bit: u64) -> BitPumpMsb32<'_> {
225 BitPumpMsb32::at_bit(self.scan, bit)
226 }
227 fn position(p: &BitPumpMsb32<'_>) -> u64 {
228 p.bit_position()
229 }
230 #[inline(always)]
231 fn decode(&self, p: &mut BitPumpMsb32<'_>, _phase: usize, out: &mut [u16]) -> bool {
232 let before = p.bit_position();
233 let lens = (self.table.decode_symbol(p), self.table.decode_symbol(p));
234 let (Ok(l0), Ok(l1)) = lens else {
235 if p.bit_position() == before {
236 p.skip(1);
237 }
238 return false;
239 };
240 if l0 > 16 || l1 > 16 {
241 return false;
242 }
243 out[0] = diff(p, l0) as u16;
244 out[1] = diff(p, l1) as u16;
245 true
246 }
247}
248
249fn decode_parallel(
252 data: &[u8],
253 hdr: &HasselbladHeader,
254 out: &mut [u16],
255 width: usize,
256 base: i32,
257 chunk: Option<usize>,
258) -> bool {
259 let Some(scan) = data.get(hdr.scan_offset..) else { return false };
260 let Some(chunk) = chunk.or_else(|| crate::par_entropy::default_chunk(scan.len())) else { return false };
261 let src = PairScan { scan, table: &hdr.table };
262 let Some(values) = crate::par_entropy::decode(&src, out.len() / 2, chunk) else { return false };
263 let start = (0x8000 + base) as u16;
264 flashraw_core::par::for_each_row_chunk(out, width, 16, true, |r0, rows| {
265 for (i, row) in rows.chunks_exact_mut(width).enumerate() {
266 values.copy_to((r0 + i) * width, row);
267 let (mut p0, mut p1) = (start, start);
268 for px in row.chunks_exact_mut(2) {
269 p0 = p0.wrapping_add(px[0]);
270 p1 = p1.wrapping_add(px[1]);
271 px[0] = p0;
272 px[1] = p1;
273 }
274 }
275 });
276 true
277}
278
279fn decode_left(
282 bits: &mut BitPumpMsb32,
283 t: &HuffTable,
284 out: &mut [u16],
285 width: usize,
286 height: usize,
287 start: i32,
288) -> Result<()> {
289 for (row, dst) in out.chunks_exact_mut(width).take(height).enumerate() {
290 let (mut p0, mut p1) = (start, start);
291 for px in dst.chunks_exact_mut(2) {
292 let l0 = t.decode_symbol(bits)?;
293 let l1 = t.decode_symbol(bits)?;
294 if l0 > 16 || l1 > 16 {
295 return Err(Error::corrupt("Hasselblad: difference length > 16"));
296 }
297 p0 = p0.wrapping_add(diff(bits, l0));
298 p1 = p1.wrapping_add(diff(bits, l1));
299 px[0] = p0 as u16;
300 px[1] = p1 as u16;
301 }
302 if bits.exhausted() {
303 return Err(Error::corrupt(format!("Hasselblad: data ends at row {row}/{height}")));
304 }
305 }
306 Ok(())
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 fn encode(width: usize, height: usize, samples: &[u16]) -> Vec<u8> {
315 let counts: [u8; 16] = [0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
317 let values: Vec<u8> = (0..=16).collect();
318 let mut out = vec![0xFF, 0xD8];
319 out.extend([
320 0xFF,
321 0xC3,
322 0,
323 11,
324 16,
325 (height >> 8) as u8,
326 height as u8,
327 (width >> 8) as u8,
328 width as u8,
329 1,
330 1,
331 0x11,
332 0,
333 ]);
334 out.extend([0xFF, 0xC4, 0, (2 + 17 + values.len()) as u8, 0x00]);
335 out.extend(counts);
336 out.extend(&values);
337 out.extend([0xFF, 0xDA, 0, 8, 1, 1, 0, 1, 0, 0]);
338 let mut words: Vec<u32> = vec![];
339 let (mut acc, mut n) = (0u64, 0u32);
340 let mut put = |v: u32, b: u32, words: &mut Vec<u32>| {
341 if b == 0 {
342 return;
343 }
344 acc = (acc << b) | (v as u64 & ((1u64 << b) - 1));
345 n += b;
346 while n >= 32 {
347 words.push((acc >> (n - 32)) as u32);
348 n -= 32;
349 }
350 };
351 for row in 0..height {
352 for col in (0..width).step_by(2) {
353 let mut ds = [0i32; 2];
354 for (k, dk) in ds.iter_mut().enumerate() {
355 let s = col + k;
356 let pred = if col > 0 { samples[row * width + s - 2] as i32 } else { 0x8000 };
357 *dk = samples[row * width + s] as i32 - pred;
358 }
359 let lens: Vec<u32> =
360 ds.iter().map(|&d| if d == 0 { 0 } else { 32 - (d.unsigned_abs()).leading_zeros() }).collect();
361 for &l in &lens {
362 put(l, 5, &mut words); }
364 for k in 0..2 {
365 let (d, l) = (ds[k], lens[k]);
366 let v = if d >= 0 { d as u32 } else { (d + (1 << l) - 1) as u32 };
367 put(v, l, &mut words);
368 }
369 }
370 }
371 put(0, 31, &mut words);
372 put(0, 32, &mut words);
373 for w in words {
374 out.extend(w.to_le_bytes());
375 }
376 out
377 }
378
379 #[test]
380 fn round_trip() {
381 let (w, h) = (16usize, 5usize);
382 let samples: Vec<u16> = (0..w * h).map(|i| ((i * 2654435761usize) >> 7) as u16 & 0x3FFF).collect();
383 let data = encode(w, h, &samples);
384 let hdr = HasselbladHeader::parse(&data).unwrap();
385 assert_eq!((hdr.width, hdr.height, hdr.predictor), (w as u32, h as u32, 1));
386 let mut out = vec![0u16; w * h];
387 decode(&data, &hdr, &mut out, w, h, 0).unwrap();
388 assert_eq!(out, samples);
389 }
390
391 #[test]
392 fn truncated_and_garbage() {
393 let (w, h) = (16usize, 40usize);
394 let samples: Vec<u16> = (0..w * h).map(|i| (i * 977 % 60000) as u16).collect();
395 let data = encode(w, h, &samples);
396 let hdr = HasselbladHeader::parse(&data).unwrap();
397 let mut out = vec![0u16; w * h];
398 assert!(decode(&data[..hdr.scan_offset + 20], &hdr, &mut out, w, h, 0).is_err());
399 for n in 0..hdr.scan_offset {
400 let _ = HasselbladHeader::parse(&data[..n]);
401 }
402 let mut g = data.clone();
403 for b in g[hdr.scan_offset..].iter_mut() {
404 *b = b.wrapping_mul(31).wrapping_add(7);
405 }
406 let _ = decode(&g, &hdr, &mut out, w, h, 0);
407 }
408
409 #[test]
410 fn parallel_matches_sequential() {
411 let (w, h) = (64usize, 40usize);
412 let mut x = 7u32;
413 let samples: Vec<u16> = (0..w * h)
414 .map(|i| {
415 x = x.wrapping_mul(1103515245).wrapping_add(12345);
416 (0x8000 + (i % w) as i32 * 3 + ((x >> 20) & 63) as i32) as u16
417 })
418 .collect();
419 let data = encode(w, h, &samples);
420 let hdr = HasselbladHeader::parse(&data).unwrap();
421 let mut seq = vec![0u16; w * h];
422 decode(&data, &hdr, &mut seq, w, h, 0).unwrap();
423 for chunk in [5usize, 37, 200, 1 << 20] {
424 let mut par = vec![0u16; w * h];
425 assert!(decode_parallel(&data, &hdr, &mut par, w, 0, Some(chunk)), "chunk {chunk}");
426 assert_eq!(par, seq, "chunk {chunk}");
427 }
428 }
429}