1use crate::nikon_huffman::{DirectHuff, PredictiveParams, decode_predictive};
14use flashraw_core::{Error, Result, par};
15
16#[derive(Clone)]
19pub struct Ph1Bits<'a> {
20 data: &'a [u8],
21 pos: usize,
23 buf: u64,
24 vbits: u32,
25 overrun: bool,
26}
27
28impl<'a> Ph1Bits<'a> {
29 pub fn new(data: &'a [u8], pos: usize) -> Self {
30 Ph1Bits { data, pos, buf: 0, vbits: 0, overrun: false }
31 }
32
33 pub fn pos(&self) -> usize {
35 self.pos
36 }
37
38 pub fn reset_at(&mut self, pos: usize) {
40 self.pos = pos;
41 self.buf = 0;
42 self.vbits = 0;
43 }
44
45 pub fn overrun(&self) -> bool {
46 self.overrun
47 }
48
49 #[inline(always)]
50 fn fetch(&mut self) {
51 let w = match self.data.get(self.pos..self.pos + 4) {
52 Some(s) => u32::from_le_bytes([s[0], s[1], s[2], s[3]]),
53 None => {
54 self.overrun = true;
55 u32::MAX
56 }
57 };
58 self.pos += 4;
59 self.buf = (self.buf << 32) | w as u64;
60 self.vbits += 32;
61 }
62
63 #[inline(always)]
65 pub fn get(&mut self, n: u32) -> u32 {
66 if n == 0 {
67 return 0;
68 }
69 let n = n.min(32);
70 if self.vbits < n {
71 self.fetch();
72 }
73 let v = ((self.buf << (64 - self.vbits)) >> (64 - n)) as u32;
74 self.vbits -= n;
75 v
76 }
77}
78
79pub fn decode_v0_reference(
82 src: &[u8],
83 row_offsets: &[u32],
84 width: usize,
85 height: usize,
86 out: &mut [u16],
87) -> Result<()> {
88 if out.len() < width * height || row_offsets.len() < height {
89 return Err(Error::InvalidArgument("Samsung v0: buffer or offsets too small".into()));
90 }
91 let max_index = width * (height + 7);
92 let raw_len = width * height;
93 for (row, &row_offset) in row_offsets.iter().enumerate().take(height) {
94 let mut bits = Ph1Bits::new(src, row_offset as usize);
95 let mut len = [if row < 2 { 7i32 } else { 4 }; 4];
96 let mut col = 0usize;
97 while col < width {
98 let dir = bits.get(1);
99 let mut op = [0u32; 4];
100 for o in op.iter_mut() {
101 *o = bits.get(2);
102 }
103 for c in 0..4 {
104 match op[c] {
105 3 => len[c] = bits.get(4) as i32,
106 2 => len[c] -= 1,
107 1 => len[c] += 1,
108 _ => {}
109 }
110 }
111 for c in (0..16).step_by(2).chain((1..16).step_by(2)) {
113 let i = len[((c & 1) << 1) | (c >> 3)];
114 let dest = row * width + col + c;
115 let src_idx: Option<usize> = if dir != 0 {
118 let up = if c & 1 == 0 { 1 } else { 2 };
119 row.checked_sub(up).map(|r| r * width + col + c)
120 } else if col > 0 {
121 Some(row * width + col - if c & 1 == 0 { 2 } else { 1 })
122 } else {
123 None
124 };
125 let pred = match src_idx {
126 Some(s) if s < max_index && dest < max_index => {
127 if s < raw_len {
128 out[s] as i32
129 } else {
130 0
131 }
132 }
133 Some(_) => {
134 continue;
136 }
137 None if dir != 0 => continue,
138 None => 128,
139 };
140 let d = if i > 0 {
141 let i = i.min(32) as u32;
142 let v = bits.get(i);
143 ((v << (32 - i)) as i32) >> (32 - i)
144 } else {
145 0
146 };
147 if dest < raw_len {
148 out[dest] = (d.wrapping_add(pred)) as u16;
149 }
150 }
151 col += 16;
152 }
153 }
154 let mut row = 0;
156 while row + 1 < height {
157 let mut col = 0;
158 while col + 1 < width {
159 out.swap(row * width + col + 1, (row + 1) * width + col);
160 col += 2;
161 }
162 row += 2;
163 }
164 Ok(())
165}
166
167#[derive(Clone, Copy, Default)]
169struct V0Op {
170 diff: i32,
172 kind: u8,
174}
175
176fn parse_v0_row(src: &[u8], offset: usize, row: usize, width: usize, max_index: usize, ops: &mut [V0Op]) {
178 let mut bits = Ph1Bits::new(src, offset);
179 let mut len = [if row < 2 { 7i32 } else { 4 }; 4];
180 let mut col = 0usize;
181 while col < width {
182 let dir = bits.get(1);
183 let mut op = [0u32; 4];
184 for o in op.iter_mut() {
185 *o = bits.get(2);
186 }
187 for c in 0..4 {
188 match op[c] {
189 3 => len[c] = bits.get(4) as i32,
190 2 => len[c] -= 1,
191 1 => len[c] += 1,
192 _ => {}
193 }
194 }
195 for c in (0..16).step_by(2).chain((1..16).step_by(2)) {
196 let i = len[((c & 1) << 1) | (c >> 3)];
197 let dest = row * width + col + c;
200 let src_idx: Option<usize> = if dir != 0 {
201 let up = if c & 1 == 0 { 1 } else { 2 };
202 row.checked_sub(up).map(|r| r * width + col + c)
203 } else if col > 0 {
204 Some(row * width + col - if c & 1 == 0 { 2 } else { 1 })
205 } else {
206 None
207 };
208 let kind = match src_idx {
209 Some(s) if s < max_index && dest < max_index => {
210 if dir != 0 {
211 1
212 } else {
213 0
214 }
215 }
216 Some(_) => 3,
217 None if dir != 0 => 3,
218 None => 2,
219 };
220 if kind == 3 {
221 if col + c < ops.len() {
222 ops[col + c] = V0Op { diff: 0, kind };
223 }
224 continue;
225 }
226 let d = if i > 0 {
227 let i = i.min(32) as u32;
228 let v = bits.get(i);
229 ((v << (32 - i)) as i32) >> (32 - i)
230 } else {
231 0
232 };
233 if col + c < ops.len() {
234 ops[col + c] = V0Op { diff: d, kind };
235 }
236 }
237 col += 16;
238 }
239}
240
241pub fn decode_v0(src: &[u8], row_offsets: &[u32], width: usize, height: usize, out: &mut [u16]) -> Result<()> {
249 decode_v0_with(src, row_offsets, width, height, out, true)
250}
251
252pub fn decode_v0_with(
261 src: &[u8],
262 row_offsets: &[u32],
263 width: usize,
264 height: usize,
265 out: &mut [u16],
266 parallel: bool,
267) -> Result<()> {
268 if out.len() < width * height || row_offsets.len() < height {
269 return Err(Error::InvalidArgument("Samsung v0: buffer or offsets too small".into()));
270 }
271 if width == 0 || height == 0 {
272 return Ok(());
273 }
274 let head = height.min(2);
275 decode_v0_head(src, row_offsets, width, height, head, out);
276 if height > head {
277 let nblk = width.div_ceil(16);
278 let (top, rest) = out[..width * height].split_at_mut(head * width);
279 let mut dirs = vec![0u8; nblk * (height - head)];
281 const ROWS: usize = 16;
282 let bounds: Vec<(usize, usize)> =
283 (0..height - head).step_by(ROWS).map(|r| (r * width, (r + ROWS).min(height - head) * width)).collect();
284 let parts = par::split_disjoint(rest, &bounds).unwrap_or_default();
285 let jobs: Vec<(usize, &mut [u16], &mut [u8])> = parts
286 .into_iter()
287 .zip(dirs.chunks_mut(ROWS * nblk))
288 .enumerate()
289 .map(|(k, (o, d))| (k * ROWS, o, d))
290 .collect();
291 par::map(jobs, parallel, |(r0, o, d)| {
292 for (i, (row, dir)) in o.chunks_exact_mut(width).zip(d.chunks_exact_mut(nblk)).enumerate() {
293 parse_v0_residuals(src, row_offsets[head + r0 + i] as usize, width, row, dir);
294 }
295 });
296 let block = nblk.div_ceil(4 * par::threads().max(1)).max(16) * 16;
298 let geo = par::Wavefront { row_len: width, halo: 2, margin: 0, block };
299 let dirs = &dirs;
300 par::wavefront(rest, top, geo, parallel, |first, band, above, _, a, b| {
301 let aw = b - a;
302 let na = above.len() / aw;
303 for i in 0..band.len() / width {
304 let (done, cur) = band.split_at_mut(i * width);
305 let cur = &mut cur[..width];
306 let up = |k: usize| -> &[u16] {
307 if i >= k { &done[(i - k) * width + a..][..aw] } else { &above[(na + i - k) * aw..][..aw] }
308 };
309 let (up1, up2) = (up(1), up(2));
310 let dir = &dirs[(first + i) * nblk..][..nblk];
311 for col in (a..b).step_by(16) {
312 let e = (col + 16).min(width);
313 if dir[col / 16] != 0 {
314 for x in col..e {
316 let p = if x & 1 == 0 { up1[x - a] } else { up2[x - a] };
317 cur[x] = cur[x].wrapping_add(p);
318 }
319 } else {
320 let (pe, po) = if col == 0 { (128, 128) } else { (cur[col - 2], cur[col - 1]) };
322 for x in col..e {
323 cur[x] = cur[x].wrapping_add(if x & 1 == 0 { pe } else { po });
324 }
325 }
326 }
327 }
328 });
329 }
330 let pairs = height / 2 * 2 * width;
332 par::for_each_row_chunk(&mut out[..pairs], 2 * width, 64, parallel, |_, rows| {
333 for pair in rows.chunks_exact_mut(2 * width) {
334 let (r0, r1) = pair.split_at_mut(width);
335 for col in (0..width.saturating_sub(1)).step_by(2) {
336 std::mem::swap(&mut r0[col + 1], &mut r1[col]);
337 }
338 }
339 });
340 Ok(())
341}
342
343fn parse_v0_residuals(src: &[u8], offset: usize, width: usize, out: &mut [u16], dirs: &mut [u8]) {
347 let mut bits = Ph1Bits::new(src, offset);
348 let mut len = [4i32; 4];
349 for (blk, col) in (0..width).step_by(16).enumerate() {
350 dirs[blk] = bits.get(1) as u8;
351 let mut op = [0u32; 4];
352 for o in op.iter_mut() {
353 *o = bits.get(2);
354 }
355 for c in 0..4 {
356 match op[c] {
357 3 => len[c] = bits.get(4) as i32,
358 2 => len[c] -= 1,
359 1 => len[c] += 1,
360 _ => {}
361 }
362 }
363 for c in (0..16).step_by(2).chain((1..16).step_by(2)) {
364 let i = len[((c & 1) << 1) | (c >> 3)];
365 let d = if i > 0 {
366 let i = i.min(32) as u32;
367 let v = bits.get(i);
368 ((v << (32 - i)) as i32) >> (32 - i)
369 } else {
370 0
371 };
372 if let Some(o) = out.get_mut(col + c) {
373 *o = d as u16;
374 }
375 }
376 }
377}
378
379fn decode_v0_head(src: &[u8], row_offsets: &[u32], width: usize, height: usize, head: usize, out: &mut [u16]) {
382 let max_index = width * (height + 7);
383 let raw_len = width * height;
384 let mut ops = vec![V0Op::default(); width.div_ceil(16) * 16];
385 for row in 0..head {
386 parse_v0_row(src, row_offsets[row] as usize, row, width, max_index, &mut ops);
387 let mut col = 0usize;
388 while col < width {
389 for c in 0..16 {
390 let dest = row * width + col + c;
391 let op = ops[col + c];
392 let pred = match op.kind {
393 1 => {
394 let up = if c & 1 == 0 { 1 } else { 2 };
395 let s = (row - up) * width + col + c;
396 if s < raw_len { out[s] as i32 } else { 0 }
397 }
398 0 => {
399 let s = row * width + col - if c & 1 == 0 { 2 } else { 1 };
400 if s < raw_len { out[s] as i32 } else { 0 }
401 }
402 2 => 128,
403 _ => continue,
404 };
405 if dest < raw_len {
406 out[dest] = (op.diff.wrapping_add(pred)) as u16;
407 }
408 }
409 col += 16;
410 }
411 }
412}
413
414const V1_TABLE: [u16; 14] =
416 [0x304, 0x307, 0x206, 0x205, 0x403, 0x600, 0x709, 0x80a, 0x90b, 0xa0c, 0xa0d, 0x501, 0x408, 0x402];
417
418fn v1_table() -> DirectHuff {
419 let mut direct = Vec::with_capacity(1024);
420 for &e in &V1_TABLE {
421 for _ in 0..(1024 >> (e >> 8)) {
422 direct.push(e);
423 }
424 }
425 direct.resize(1024, 0);
426 DirectHuff::from_direct(10, direct)
427}
428
429pub fn decode_v1(src: &[u8], width: usize, height: usize, out: &mut [u16]) -> Result<Option<usize>> {
432 let t = v1_table();
433 let p = PredictiveParams { width, rows: height, tables: &[(0, &t)], vpred: [[0; 2]; 2] };
434 Ok(decode_predictive(src, &p, out, width, |h| h)?.dead_row())
435}
436
437pub fn decode_v2(src: &[u8], width: usize, height: usize, out: &mut [u16]) -> Result<()> {
439 if height < 436 || width < 646 {
440 return Err(Error::corrupt("Samsung v2: image too small"));
441 }
442 if out.len() < width * height {
443 return Err(Error::InvalidArgument("Samsung v2: output too small".into()));
444 }
445 let mut bits = Ph1Bits::new(src, 0);
446 bits.get(16);
447 bits.get(4);
448 let bit_depth = bits.get(4) + 1;
449 let datamax = (1i32 << bit_depth) - 1;
450 bits.get(8);
451 bits.get(16);
452 bits.get(16);
453 bits.get(16);
454 bits.get(4);
455 let optflags = bits.get(4);
456 bits.get(16);
457 bits.get(8);
458 bits.get(2);
459 let init_val = bits.get(14) as u16;
460 let hdr = V2Hdr { bit_depth, datamax, optflags, init_val };
461 for row in 0..height {
462 let p = bits.pos();
464 bits.reset_at(p + ((16 - p % 16) % 16));
465 v2_row(&mut bits, &hdr, row, width, height, out)?;
466 if bits.overrun() {
467 return Err(Error::corrupt(format!("Samsung v2: truncated data at row {row}/{height}")));
468 }
469 }
470 Ok(())
471}
472
473struct V2Hdr {
474 bit_depth: u32,
475 datamax: i32,
476 optflags: u32,
477 init_val: u16,
478}
479
480const V2_ORDER_EVEN: [u8; 16] = {
482 let mut o = [0u8; 16];
483 let mut i = 0;
484 while i < 16 {
485 o[i] = (((i & 7) << 1) + (i >> 3)) as u8;
486 i += 1;
487 }
488 o
489};
490const V2_ORDER_ODD: [u8; 16] = {
491 let mut o = [0u8; 16];
492 let mut i = 0;
493 while i < 16 {
494 o[i] = (((i & 7) << 1) + 1 - (i >> 3)) as u8;
495 i += 1;
496 }
497 o
498};
499
500#[inline(always)]
502fn v2_row(bits: &mut Ph1Bits, h: &V2Hdr, row: usize, width: usize, height: usize, out: &mut [u16]) -> Result<()> {
503 let V2Hdr { bit_depth, datamax, optflags, init_val } = *h;
504 let total = (width * height) as i64;
505 const MOTION_OFFSET: [i64; 7] = [-4, -2, -2, 0, 0, 2, 4];
506 const MOTION_AVERAGE: [bool; 7] = [false, false, true, false, true, false, false];
507 const SCALEVALS: [i32; 3] = [0, -2, 2];
508 let rowstart = row * width;
509 let mut motion = 7u32;
510 let mut scale: i32 = 0;
511 let mut mode = [[if row < 2 { 7u32 } else { 4 }; 2]; 3];
512 let mut col = 0usize;
513 while col + 15 < width {
514 if optflags & 4 == 0 && col & 63 == 0 {
515 let i = bits.get(2);
516 if i < 3 {
517 scale += SCALEVALS[i as usize];
518 } else {
519 scale = bits.get(12) as i32;
520 }
521 }
522 if optflags & 2 != 0 {
523 motion = if bits.get(1) != 0 { 3 } else { 7 };
524 } else if bits.get(1) == 0 {
525 motion = bits.get(3);
526 }
527 let mut base = [0u16; 16];
530 if motion == 7 {
531 let (a, b) =
533 if col == 0 { (init_val, init_val) } else { (out[rowstart + col - 2], out[rowstart + col - 1]) };
534 for px in base.chunks_exact_mut(2) {
535 px[0] = a;
536 px[1] = b;
537 }
538 } else {
539 if row < 2 {
540 return Err(Error::corrupt("Samsung v2: motion prediction on the first rows"));
541 }
542 let m = motion as usize;
543 let avg = MOTION_AVERAGE[m];
544 let (up2, up1) = (((row - 2) * width) as i64, ((row - 1) * width) as i64);
547 let mut idx = [0i64; 16];
548 for (i, x) in idx.iter_mut().enumerate() {
549 let c = (col + i) as i64 + MOTION_OFFSET[m];
550 *x = if (row + i) & 1 == 1 { up2 + c } else { up1 + c + 1 - 2 * (i as i64 & 1) };
551 }
552 let (lo, hi) = idx.iter().fold((i64::MAX, i64::MIN), |(a, b), &x| (a.min(x), b.max(x)));
553 if lo < 0 || hi + if avg { 2 } else { 0 } >= total {
554 return Err(Error::corrupt("Samsung v2: motion reference out of range"));
555 }
556 for (b, &x) in base.iter_mut().zip(&idx) {
557 let r = x as usize;
558 *b = if avg { ((out[r] as u32 + out[r + 2] as u32 + 1) >> 1) as u16 } else { out[r] };
559 }
560 }
561 let mut diff_bits = [0u32; 4];
562 if optflags & 1 != 0 || bits.get(1) == 0 {
563 let mut flags = [0u32; 4];
564 for f in flags.iter_mut() {
565 *f = bits.get(2);
566 }
567 for i in 0..4 {
568 let colornum = if row % 2 == 1 { i >> 1 } else { ((i >> 1) + 2) % 3 };
569 diff_bits[i] = match flags[i] {
570 0 => mode[colornum][0],
571 1 => mode[colornum][0].wrapping_add(1),
572 2 => mode[colornum][0].wrapping_sub(1),
573 _ => bits.get(4),
574 };
575 mode[colornum][0] = mode[colornum][1];
576 mode[colornum][1] = diff_bits[i];
577 if diff_bits[i] > bit_depth + 1 {
578 return Err(Error::corrupt("Samsung v2: invalid difference length"));
579 }
580 }
581 }
582 let order = if row % 2 == 1 { &V2_ORDER_ODD } else { &V2_ORDER_EVEN };
585 let mut raw = [0i32; 16];
586 for (i, &o) in order.iter().enumerate() {
587 let len = diff_bits[i >> 2];
588 if len != 0 {
589 let sign_mask = 1i32 << (len - 1);
590 let v = bits.get(len) as i32;
591 raw[o as usize] = (v ^ sign_mask).wrapping_sub(sign_mask);
592 }
593 }
594 let mul = scale.wrapping_mul(2).wrapping_add(1);
595 let blk = &mut out[rowstart + col..rowstart + col + 16];
596 for ((dst, &b), &r) in blk.iter_mut().zip(&base).zip(&raw) {
597 *dst = (b as i32 + r.wrapping_mul(mul).wrapping_add(scale)).clamp(0, datamax) as u16;
598 }
599 col += 16;
600 }
601 Ok(())
602}
603
604#[cfg(test)]
605mod tests {
606 use super::*;
607
608 #[test]
609 fn ph1_reader_lazy_fetch() {
610 let data = [0x78u8, 0x56, 0x34, 0x12, 0xEF, 0xCD, 0xAB, 0x90];
611 let mut b = Ph1Bits::new(&data, 0);
612 assert_eq!(b.get(8), 0x12);
613 assert_eq!(b.pos(), 4);
614 assert_eq!(b.get(24), 0x345678);
615 assert_eq!(b.pos(), 4);
616 assert_eq!(b.get(4), 0x9);
617 assert_eq!(b.pos(), 8);
618 assert!(!b.overrun());
619 b.get(32);
620 b.get(32);
621 assert!(b.overrun());
622 }
623
624 #[test]
625 fn v1_table_is_complete() {
626 let total: usize = V1_TABLE.iter().map(|&e| 1024 >> (e >> 8)).sum();
627 assert_eq!(total, 1024);
628 let mut out = vec![0u16; 64 * 4];
629 decode_v1(&[0xA5; 300], 64, 4, &mut out).unwrap();
630 }
631
632 #[test]
633 fn v0_constant_rows() {
634 let width = 32;
636 let height = 4;
637 let src = vec![0u8; 4096];
638 let offsets: Vec<u32> = (0..height as u32).map(|r| r * 64).collect();
639 let mut out = vec![0u16; width * height];
640 decode_v0(&src, &offsets, width, height, &mut out).unwrap();
641 assert!(out.iter().all(|&v| v == 128));
642 }
643
644 #[test]
645 fn v0_two_phase_matches_reference() {
646 let mut x: u64 = 0x1234_5678_9abc_def1;
647 let sizes = [(32usize, 6usize), (48, 9), (100, 13), (16, 2), (16, 1), (7, 5), (1002, 301), (5546 / 4, 97)];
648 for (width, height) in sizes {
649 let src: Vec<u8> = (0..width * height * 8 + 64)
650 .map(|_| {
651 x ^= x << 13;
652 x ^= x >> 7;
653 x ^= x << 17;
654 if x % 5 == 0 { x as u8 } else { (x as u8) & 0x93 }
656 })
657 .collect();
658 let row_bytes = width * 4;
659 let offsets: Vec<u32> = (0..height as u32).map(|r| r * row_bytes as u32).collect();
660 let mut want = vec![7u16; width * height];
662 decode_v0_reference(&src, &offsets, width, height, &mut want).unwrap();
663 for parallel in [true, false] {
664 let mut got = vec![7u16; width * height];
665 decode_v0_with(&src, &offsets, width, height, &mut got, parallel).unwrap();
666 assert_eq!(want, got, "{width}x{height} parallel {parallel}");
667 }
668 }
669 }
670
671 #[test]
672 fn v2_rejects_small_and_garbage() {
673 let mut out = vec![0u16; 10];
674 assert!(decode_v2(&[0; 100], 10, 1, &mut out).is_err());
675 let (w, h) = (656usize, 440usize);
676 let mut out = vec![0u16; w * h];
677 let r = decode_v2(&[0x5Au8; 2000], w, h, &mut out);
679 assert!(r.is_err());
680 }
681}