1use flashraw_core::bits::{BitPumpMsb, BitRead};
29use flashraw_core::par::{self, GridWriter};
30use flashraw_core::{Error, Rect, Result};
31
32pub const HEADER_SIZE: usize = 16;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct FujiHeader {
38 pub lossless: bool,
39 pub xtrans: bool,
41 pub bits: u32,
42 pub height: u32,
43 pub rounded_width: u32,
44 pub width: u32,
45 pub block_size: u32,
46 pub blocks: u32,
47 pub lines: u32,
49}
50
51impl FujiHeader {
52 pub fn parse(d: &[u8]) -> Result<Self> {
54 let h = d.get(..HEADER_SIZE).ok_or_else(|| Error::corrupt("truncated Fuji compressed header"))?;
55 let be16 = |i: usize| u16::from_be_bytes([h[i], h[i + 1]]) as u32;
56 let signature = be16(0);
57 let lossless = h[2];
58 let raw_type = h[3];
59 let bits = h[4] as u32;
60 let height = be16(5);
61 let rounded_width = be16(7);
62 let width = be16(9);
63 let block_size = be16(11);
64 let blocks = h[13] as u32;
65 let lines = be16(14);
66 let ok = signature == 0x4953
67 && lossless <= 1
68 && (6..=0x4002).contains(&height)
69 && height % 6 == 0
70 && block_size == 0x300
71 && (0x300..=0x4200).contains(&width)
72 && width % 24 == 0
73 && rounded_width <= 0x4200
74 && rounded_width >= block_size
75 && rounded_width % block_size == 0
76 && rounded_width - width < block_size
77 && blocks > 0
78 && blocks <= 0x10
79 && blocks == rounded_width / block_size
80 && lines > 0
81 && lines <= 0xAAB
82 && lines == height / 6
83 && matches!(bits, 12 | 14 | 16)
84 && matches!(raw_type, 0 | 16);
85 if !ok {
86 return Err(Error::corrupt("invalid Fuji compressed header"));
87 }
88 Ok(FujiHeader {
89 lossless: lossless == 1,
90 xtrans: raw_type == 16,
91 bits,
92 height,
93 rounded_width,
94 width,
95 block_size,
96 blocks,
97 lines,
98 })
99 }
100
101 fn line_width(&self) -> usize {
103 if self.xtrans { self.block_size as usize * 2 / 3 } else { self.block_size as usize / 2 }
104 }
105
106 pub fn block_width(&self, i: u32) -> u32 {
108 if i + 1 == self.blocks { self.width - self.block_size * i } else { self.block_size }
109 }
110}
111
112pub type ColorTile = [[u8; 6]; 6];
115
116pub struct FujiCompressed<'a> {
118 pub header: FujiHeader,
119 blocks: Vec<&'a [u8]>,
121 q_bases: Vec<&'a [u8]>,
123}
124
125impl<'a> FujiCompressed<'a> {
126 pub fn new(data: &'a [u8]) -> Result<Self> {
128 let header = FujiHeader::parse(data)?;
129 let body = &data[HEADER_SIZE..];
130 let nb = header.blocks as usize;
131 let sizes_len = 4 * nb;
132 let sizes = body.get(..sizes_len).ok_or_else(|| Error::corrupt("truncated Fuji block size table"))?;
133 let mut pos = (sizes_len + 15) & !15;
134 let mut q_bases = Vec::new();
135 if !header.lossless {
136 let step = (header.lines as usize + 15) & !15;
137 for b in 0..nb {
138 let start = pos + b * step;
139 let q = body.get(start..start + header.lines as usize);
140 q_bases.push(q.ok_or_else(|| Error::corrupt("truncated Fuji lossy quantisation table"))?);
142 }
143 pos += nb * step;
144 }
145 let mut blocks = Vec::with_capacity(nb);
146 for b in 0..nb {
147 let size =
148 u32::from_be_bytes([sizes[4 * b], sizes[4 * b + 1], sizes[4 * b + 2], sizes[4 * b + 3]]) as usize;
149 let start = pos.min(body.len());
152 let end = pos.saturating_add(size).min(body.len());
153 blocks.push(&body[start..end]);
154 pos = pos.saturating_add(size);
155 }
156 Ok(FujiCompressed { header, blocks, q_bases })
157 }
158
159 pub fn block_ranges(&self, base: &'a [u8]) -> Vec<(usize, usize)> {
161 self.blocks.iter().map(|b| (b.as_ptr() as usize - base.as_ptr() as usize, b.len())).collect()
162 }
163
164 pub fn decode(&self, out: &mut [u16], width: u32, height: u32, colors: &ColorTile, parallel: bool) -> Result<()> {
167 let h = &self.header;
168 if width != h.width || height != h.height {
169 return Err(Error::InvalidArgument("Fuji compressed output size does not match the stream".into()));
170 }
171 self.decode_region(out, Rect::new(0, 0, width, height), colors, parallel)
172 }
173
174 pub fn decode_region(&self, out: &mut [u16], region: Rect, colors: &ColorTile, parallel: bool) -> Result<()> {
178 let h = &self.header;
179 let (w, ht) = (region.width as usize, region.height as usize);
180 if out.len() < w * ht || region.right() > h.width || region.bottom() > h.height {
181 return Err(Error::InvalidArgument("Fuji compressed region outside the image or buffer too small".into()));
182 }
183 if region.is_empty() {
184 return Ok(());
185 }
186 let common = Common::new(h, &self.q_bases)?;
187 let map = OutputMap::new(h, colors);
188 let grid = GridWriter::new(
189 out,
190 region.width,
191 region.height,
192 1,
193 (region.x, region.y),
194 h.block_size,
195 h.height,
196 h.width,
197 h.height,
198 )
199 .ok_or_else(|| Error::InvalidArgument("invalid Fuji output grid".into()))?;
200 let needed_lines = (region.bottom() as usize).div_ceil(6).min(h.lines as usize);
201 let errors = std::sync::Mutex::new(None);
202 par::for_each_index(self.blocks.len(), parallel, |b| {
203 let Some(mut tile) = grid.claim(b as u32, 0) else { return };
205 let (tw, th) = tile.size();
206 let (sx, sy) = (tile.skip_x as usize, tile.skip_y as usize);
207 let q = self.q_bases.get(b).copied().unwrap_or(&[]);
208 let emit = |line: usize, lines: &[u16]| {
209 for r in 0..6 {
210 let y = line * 6 + r;
211 if y < sy || y - sy >= th {
212 continue;
213 }
214 let row = tile.row(y - sy);
215 let idx = map.idx[r].get(sx..sx + tw).unwrap_or(&[]);
216 for (dst, &src) in row.iter_mut().zip(idx.iter()) {
217 *dst = lines.get(src as usize).copied().unwrap_or(0);
218 }
219 }
220 };
221 let res = if h.lossless {
222 decode_block::<false>(&common, self.blocks[b], q, needed_lines, emit)
223 } else {
224 decode_block::<true>(&common, self.blocks[b], q, needed_lines, emit)
225 };
226 if let Err(e) = res {
227 errors.lock().unwrap().get_or_insert(e);
228 }
229 });
230 match errors.into_inner().unwrap() {
231 Some(e) => Err(e),
232 None => Ok(()),
233 }
234 }
235}
236
237const R0: usize = 0;
239const R2: usize = 2;
240const R3: usize = 3;
241const R4: usize = 4;
242const G0: usize = 5;
243const G2: usize = 7;
244const G3: usize = 8;
245const G4: usize = 9;
246const G5: usize = 10;
247const G6: usize = 11;
248const G7: usize = 12;
249const B0: usize = 13;
250const B2: usize = 15;
251const B3: usize = 16;
252const B4: usize = 17;
253const LINES: usize = 18;
254
255struct OutputMap {
257 idx: [Vec<u32>; 6],
258}
259
260impl OutputMap {
261 fn new(h: &FujiHeader, colors: &ColorTile) -> Self {
262 let stride = h.line_width() + 2;
263 let bs = h.block_size as usize;
264 let idx = std::array::from_fn(|r| {
265 (0..bs)
266 .map(|p| {
267 let c = colors[r][p % 6];
268 let line = match c {
269 0 => R2 + r / 2,
270 2 => B2 + r / 2,
271 _ => G2 + r,
272 };
273 let i = if h.xtrans { (((p * 2 / 3) & !1) | ((p % 3) & 1)) + ((p % 3) >> 1) } else { p >> 1 };
274 (line * stride + 1 + i) as u32
275 })
276 .collect()
277 });
278 OutputMap { idx }
279 }
280}
281
282const LUT_LEN: usize = 1 << 17;
285type Lut = Box<[i8; LUT_LEN]>;
286
287#[derive(Clone)]
289struct QTable {
290 lut: Lut,
291 q_base: i32,
292 max_grad: i32,
293 grad_mult: i32,
294 total_values: i32,
295 raw_bits: u32,
296 max_zeros: i32,
298}
299
300fn log2ceil(v: i32) -> u32 {
301 if v <= 1 { 0 } else { 32 - ((v - 1) as u32).leading_zeros() }
302}
303
304impl QTable {
305 fn lut(qp: [i32; 5]) -> Lut {
308 let max = qp[4];
309 let mut t: Lut = vec![0; LUT_LEN].into_boxed_slice().try_into().unwrap();
310 for v in -max..=max {
311 t[v as usize & (LUT_LEN - 1)] = {
312 if v <= -qp[3] {
313 -4
314 } else if v <= -qp[2] {
315 -3
316 } else if v <= -qp[1] {
317 -2
318 } else if v < -qp[0] {
319 -1
320 } else if v <= qp[0] {
321 0
322 } else if v < qp[1] {
323 1
324 } else if v < qp[2] {
325 2
326 } else if v < qp[3] {
327 3
328 } else {
329 4
330 }
331 };
332 }
333 t
334 }
335
336 fn new(lut: Lut, q_base: i32, max_grad: i32, grad_mult: i32, total_values: i32, max_value: i32) -> Self {
337 let raw_bits = log2ceil(total_values);
338 let max_bits = 4 * log2ceil(max_value + 1) as i32;
339 QTable { lut, q_base, max_grad, grad_mult, total_values, raw_bits, max_zeros: max_bits - raw_bits as i32 - 1 }
340 }
341
342 fn main(q_base: i32, max_value: i32) -> QTable {
344 let max_val = max_value + 1;
345 let mut qp = [q_base, 3 * q_base + 0x12, 5 * q_base + 0x43, 7 * q_base + 0x114, max_value];
346 if qp[1] >= max_val || qp[1] < q_base + 1 {
347 qp[1] = q_base + 1;
348 }
349 if qp[2] < qp[1] || qp[2] >= max_val {
350 qp[2] = qp[1];
351 }
352 if qp[3] < qp[2] || qp[3] >= max_val {
353 qp[3] = qp[2];
354 }
355 let total_values = (qp[4] + 2 * q_base) / (2 * q_base + 1) + 1;
356 Self::new(Self::lut(qp), q_base, 0, 9, total_values, max_value)
357 }
358
359 #[inline(always)]
360 fn level(&self, v: i32) -> i32 {
361 self.lut[v as usize & (LUT_LEN - 1)] as i32
363 }
364
365 fn lossy_extra(max_value: i32) -> [QTable; 3] {
367 let m = max_value;
368 let pick = |limit: i32, fallback: i32| if m >= limit { limit } else { fallback };
369 let mk = |q_base: i32, max_grad: i32, total_values: i32, t: [i32; 3]| {
370 let q1 = pick(t[0], q_base + 1);
371 let q2 = pick(t[1], q1);
372 let q3 = pick(t[2], q2);
373 Self::new(Self::lut([q_base, q1, q2, q3, m]), q_base, max_grad, 3, total_values, m)
374 };
375 [
376 mk(0, 5, m + 1, [0x12, 0x43, 0x114]),
377 mk(1, 6, (m + 2) / 3 + 1, [0x15, 0x48, 0x11B]),
378 mk(2, 7, (m + 4) / 5 + 1, [0x18, 0x4D, 0x122]),
379 ]
380 }
381}
382
383struct Common {
385 xtrans: bool,
386 line_width: usize,
387 max_value: i32,
388 main: Vec<Option<QTable>>,
390 extra: [QTable; 3],
391}
392
393impl Common {
394 fn new(h: &FujiHeader, q_bases: &[&[u8]]) -> Result<Self> {
395 let max_value = (1i32 << h.bits) - 1;
396 let mut main: Vec<Option<QTable>> = vec![None; 256];
397 if h.lossless {
398 main[0] = Some(QTable::main(0, max_value));
399 } else {
400 let mut seen = [false; 256];
402 for q in q_bases.iter().flat_map(|q| q.iter()) {
403 seen[*q as usize] = true;
404 }
405 for (q, _) in seen.iter().enumerate().filter(|(_, s)| **s) {
406 main[q] = Some(QTable::main(q as i32, max_value));
407 }
408 }
409 Ok(Common {
410 xtrans: h.xtrans,
411 line_width: h.line_width(),
412 max_value,
413 main,
414 extra: QTable::lossy_extra(max_value),
415 })
416 }
417}
418
419#[derive(Clone, Copy)]
424#[repr(C, align(8))]
425struct Stat {
426 sum: i32,
427 n: u8,
428 k: u8,
429 mask: u16,
431}
432
433#[inline(always)]
434fn new_stat(sum: i32, n: i32) -> Stat {
435 let k = bit_diff(sum, n);
437 Stat { sum, n: n as u8, k: k as u8, mask: ((1u32 << k) - 1) as u16 }
438}
439
440#[derive(Clone, Copy)]
443struct Grads {
444 main: [Stat; 41],
445 lossy: [[Stat; 5]; 3],
446}
447
448const MIN_VALUE: i32 = 0x40;
449
450#[inline(always)]
452fn bit_diff(sum: i32, n: i32) -> u32 {
453 let (s, n) = (sum.max(1) as u32, n.max(1) as u32);
455 let k = n.leading_zeros().saturating_sub(s.leading_zeros());
456 let k = k + (((n as u64) << k) < s as u64) as u32;
457 k.min(15)
458}
459
460fn decode_block<const LOSSY: bool>(
462 c: &Common,
463 data: &[u8],
464 q_bases: &[u8],
465 lines: usize,
466 mut emit: impl FnMut(usize, &[u16]),
467) -> Result<()> {
468 let mut d = BlockDecoder::<LOSSY>::new(c, data, q_bases)?;
469 for line in 0..lines {
470 d.decode_line(line)?;
471 emit(line, &d.lines);
472 d.advance_line();
473 }
474 Ok(())
475}
476
477struct BlockDecoder<'c, 'a, const LOSSY: bool> {
478 c: &'c Common,
479 bits: BitPumpMsb<'a>,
480 data_len: usize,
481 lines: Vec<u16>,
483 stride: usize,
484 grads: [[Grads; 3]; 2],
486 q_bases: &'a [u8],
487 qt: &'c QTable,
489 cur_q: i32,
490 overrun: bool,
492}
493
494#[derive(Clone, Copy, PartialEq, Eq)]
495enum Even {
496 Decode,
497 Interp,
498}
499
500impl<'c, 'a, const LOSSY: bool> BlockDecoder<'c, 'a, LOSSY> {
501 fn new(c: &'c Common, data: &'a [u8], q_bases: &'a [u8]) -> Result<Self> {
502 let stride = c.line_width + 2;
503 let init = Grads { main: [new_stat(0, 1); 41], lossy: [[new_stat(0, 1); 5]; 3] };
504 let first_q = if LOSSY { q_bases.first().copied().unwrap_or(0) as usize } else { 0 };
505 let qt = c.main[first_q].as_ref().ok_or_else(|| Error::corrupt("missing Fuji quantisation table"))?;
506 let mut d = BlockDecoder {
507 c,
508 bits: BitPumpMsb::new(data),
509 data_len: data.len(),
510 lines: vec![0; LINES * stride],
511 stride,
512 grads: [[init; 3]; 2],
513 q_bases,
514 qt,
515 cur_q: -1,
516 overrun: false,
517 };
518 if LOSSY {
519 for k in 0..3 {
520 let max_diff = 2.max((c.extra[k].total_values + 0x20) >> 6);
521 for g in d.grads.iter_mut().flatten() {
522 g.lossy[k] = [new_stat(max_diff, 1); 5];
523 }
524 }
525 } else {
526 d.cur_q = 0;
527 d.init_main_grads();
528 }
529 Ok(d)
530 }
531
532 fn init_main_grads(&mut self) {
533 let max_diff = 2.max((self.qt.total_values + 0x20) >> 6);
534 for g in self.grads.iter_mut().flatten() {
535 g.main = [new_stat(max_diff, 1); 41];
536 }
537 }
538
539 fn decode_line(&mut self, line: usize) -> Result<()> {
540 if LOSSY {
541 let q = self.q_bases.get(line).copied().unwrap_or(0) as i32;
542 if line == 0 || q != self.cur_q {
543 self.qt = self.c.main[q as usize]
544 .as_ref()
545 .ok_or_else(|| Error::corrupt("missing Fuji quantisation table"))?;
546 self.cur_q = q;
547 self.init_main_grads();
548 }
549 }
550 if self.c.xtrans {
551 let rule_a = |p: usize| if p & 3 != 0 { Even::Decode } else { Even::Interp };
552 let rule_b = |p: usize| if p & 3 == 2 { Even::Interp } else { Even::Decode };
553 self.pass(R2, G2, 0, |_| Even::Interp, |_| Even::Decode);
554 self.extend(R2, R4);
555 self.extend(G2, G7);
556 self.pass(G3, B2, 1, |_| Even::Decode, |_| Even::Interp);
557 self.extend(G2, G7);
558 self.extend(B2, B4);
559 self.pass(R3, G4, 2, rule_a, |_| Even::Interp);
560 self.extend(R2, R4);
561 self.extend(G2, G7);
562 self.pass(G5, B3, 0, |_| Even::Decode, rule_b);
563 self.extend(G2, G7);
564 self.extend(B2, B4);
565 self.pass(R4, G6, 1, rule_b, |_| Even::Decode);
566 self.extend(R2, R4);
567 self.extend(G2, G7);
568 self.pass(G7, B4, 2, |_| Even::Interp, rule_a);
569 self.extend(G2, G7);
570 self.extend(B2, B4);
571 } else {
572 let d = |_: usize| Even::Decode;
573 self.pass(R2, G2, 0, d, d);
574 self.extend(R2, R4);
575 self.extend(G2, G7);
576 self.pass(G3, B2, 1, d, d);
577 self.extend(G2, G7);
578 self.extend(B2, B4);
579 self.pass(R3, G4, 2, d, d);
580 self.extend(R2, R4);
581 self.extend(G2, G7);
582 self.pass(G5, B3, 0, d, d);
583 self.extend(G2, G7);
584 self.extend(B2, B4);
585 self.pass(R4, G6, 1, d, d);
586 self.extend(R2, R4);
587 self.extend(G2, G7);
588 self.pass(G7, B4, 2, d, d);
589 self.extend(G2, G7);
590 self.extend(B2, B4);
591 }
592 if self.overrun || self.bits_consumed() > 8 * self.data_len + 7 {
595 return Err(Error::corrupt("truncated Fuji compressed block"));
596 }
597 Ok(())
598 }
599
600 fn bits_consumed(&self) -> usize {
601 (self.bits.byte_position() * 8).saturating_sub((self.bits.buffered_bits() % 8) as usize)
604 }
605
606 #[inline(never)]
610 fn pass(&mut self, a: usize, b: usize, g: usize, rule_a: impl Fn(usize) -> Even, rule_b: impl Fn(usize) -> Even) {
611 let s = self.stride;
612 let mut lines = std::mem::take(&mut self.lines);
615 let mut st = Sampler { c: self.c, qt: self.qt, bits: self.bits, overrun: false };
616 let [even, odd] = &mut self.grads;
617 let (ge, go) = (&mut even[g], &mut odd[g]);
618 let (lo, hi) = lines.split_at_mut((b - 2) * s);
620 let mut la = Lines::new(&mut lo[(a - 2) * s..], s);
621 let mut lb = Lines::new(hi, s);
622 let lw = la.width();
623 let (mut ep, mut op) = (0usize, 1usize);
624 while ep < lw || op < lw {
625 if ep < lw {
626 match rule_a(ep) {
627 Even::Decode => la.decode_even::<LOSSY>(&mut st, ge, ep),
628 Even::Interp => la.interp_even(ep),
629 }
630 match rule_b(ep) {
631 Even::Decode => lb.decode_even::<LOSSY>(&mut st, ge, ep),
632 Even::Interp => lb.interp_even(ep),
633 }
634 ep += 2;
635 }
636 if ep > 8 {
637 la.decode_odd::<LOSSY>(&mut st, go, op);
638 lb.decode_odd::<LOSSY>(&mut st, go, op);
639 op += 2;
640 }
641 }
642 self.bits = st.bits;
643 self.overrun |= st.overrun;
644 self.lines = lines;
645 }
646
647 fn extend(&mut self, first: usize, last: usize) {
649 let (s, lw) = (self.stride, self.c.line_width);
650 for l in first..=last {
651 self.lines[l * s] = self.lines[(l - 1) * s + 1];
652 self.lines[l * s + lw + 1] = self.lines[(l - 1) * s + lw];
653 }
654 }
655
656 fn advance_line(&mut self) {
659 let s = self.stride;
660 let lw = self.c.line_width;
661 for (dst, src) in [(R0, R3), (R0 + 1, R4), (G0, G6), (G0 + 1, G7), (B0, B3), (B0 + 1, B4)] {
662 self.lines.copy_within(src * s..(src + 1) * s, dst * s);
663 }
664 for (first, n) in [(R2, 3), (G2, 6), (B2, 3)] {
665 self.lines[first * s..(first + n) * s].fill(0);
666 self.lines[first * s] = self.lines[(first - 1) * s + 1];
667 self.lines[first * s + lw + 1] = self.lines[(first - 1) * s + lw];
668 }
669 }
670}
671
672struct Sampler<'c, 'a> {
674 c: &'c Common,
675 qt: &'c QTable,
677 bits: BitPumpMsb<'a>,
678 overrun: bool,
680}
681
682impl Sampler<'_, '_> {
683 #[inline(always)]
685 fn zero_run(&mut self) -> i32 {
686 let mut n = 0i32;
687 loop {
688 self.bits.fill();
689 let w = self.bits.peek_no_fill(32);
690 if w != 0 {
691 let lz = w.leading_zeros();
692 self.bits.skip_no_fill(lz + 1);
693 return n + lz as i32;
694 }
695 self.bits.skip_no_fill(32);
696 n += 32;
697 if self.bits.exhausted() {
698 self.overrun = true;
699 return n;
700 }
701 }
702 }
703
704 #[inline(always)]
707 fn decode_residual<const LOSSY: bool>(
708 &mut self,
709 grads: &mut Grads,
710 flat: i32,
711 v1: i32,
712 v2: i32,
713 interp: i32,
714 ) -> u16 {
715 let c = self.c;
716 let max = c.max_value;
717 let mut qt = self.qt;
718 let mut ctx = usize::MAX;
719 if LOSSY {
720 let mut i = 1;
722 while qt.q_base >= i && i < 4 {
723 let t = &c.extra[(i - 1) as usize];
724 if flat <= t.max_grad {
725 qt = t;
726 ctx = (i - 1) as usize;
727 break;
728 }
729 i += 1;
730 }
731 }
732 let mult = if LOSSY { qt.grad_mult } else { 9 };
734 let grad = mult * qt.level(v1) + qt.level(v2);
735 let gi = grad.unsigned_abs() as usize;
736 let st = if LOSSY && ctx != usize::MAX { grads.lossy[ctx][gi.min(4)] } else { grads.main[gi.min(40)] };
737 let (sum, n, k, mask) = (st.sum, st.n as i32, st.k as u32, st.mask as u32);
738 self.bits.refill_force();
742 let w = self.bits.peek_no_fill(32);
743 let lz = w.leading_zeros();
744 let code = if w != 0 && (lz as i32) < qt.max_zeros && lz + 1 + k <= 32 {
745 let total = lz + 1 + k;
746 let v = (w >> (32 - total)) & mask;
748 self.bits.skip_no_fill(total);
749 ((lz << k) | v) as i32
750 } else {
751 let sample = self.zero_run();
752 if sample < qt.max_zeros {
753 ((sample as u32) << k | self.bits.get(k)) as i32
754 } else {
755 self.bits.get(qt.raw_bits) as i32 + 1
756 }
757 };
758 let code = (code >> 1) ^ -(code & 1);
761 let h = (n == MIN_VALUE) as u32;
764 let (sum, n) = ((sum + code.abs()) >> h, n >> h);
765 let stat =
766 if LOSSY && ctx != usize::MAX { &mut grads.lossy[ctx][gi.min(4)] } else { &mut grads.main[gi.min(40)] };
767 *stat = new_stat(sum, n + 1);
768 let (q_base, step) = if LOSSY { (qt.q_base, 2 * qt.q_base + 1) } else { (0, 1) };
770 let mut v = interp + ((grad >> 31) | 1) * code * step;
772 if v < -q_base {
773 v += qt.total_values * step;
774 } else if v > q_base + max {
775 v -= qt.total_values * step;
776 }
777 v.clamp(0, max) as u16
778 }
779}
780
781struct Lines<'l> {
784 up2: &'l [u16],
785 up1: &'l [u16],
786 cur: &'l mut [u16],
787}
788
789impl<'l> Lines<'l> {
790 #[inline(always)]
792 fn new(block: &'l mut [u16], s: usize) -> Self {
793 let (up2, rest) = block.split_at_mut(s);
794 let (up1, rest) = rest.split_at_mut(s);
795 Lines { up2, up1, cur: &mut rest[..s] }
796 }
797
798 #[inline(always)]
800 fn width(&self) -> usize {
801 self.cur.len().saturating_sub(2)
802 }
803
804 #[inline(always)]
806 fn even_ctx(&self, pos: usize) -> (i32, i32, i32, i32) {
807 let up = &self.up1[pos..pos + 3];
808 (self.up2[pos + 1] as i32, up[0] as i32, up[1] as i32, up[2] as i32)
809 }
810
811 #[inline(always)]
812 fn interp_even(&mut self, pos: usize) {
813 let (rf, rc, rb, rd) = self.even_ctx(pos);
814 self.cur[pos + 1] = (even_prediction(rf, rc, rb, rd) >> 2) as u16;
815 }
816
817 #[inline(always)]
818 fn decode_even<const LOSSY: bool>(&mut self, st: &mut Sampler, grads: &mut Grads, pos: usize) {
819 let (rf, rc, rb, rd) = self.even_ctx(pos);
820 let interp = even_prediction(rf, rc, rb, rd) >> 2;
821 let flat = (rf - rb).abs() + (rc - rb).abs();
822 self.cur[pos + 1] = st.decode_residual::<LOSSY>(grads, flat, rb - rf, rc - rb, interp);
823 }
824
825 #[inline(always)]
826 fn decode_odd<const LOSSY: bool>(&mut self, st: &mut Sampler, grads: &mut Grads, pos: usize) {
827 let up = &self.up1[pos..pos + 3];
828 let (rc, rb, rd) = (up[0] as i32, up[1] as i32, up[2] as i32);
829 let cur = &mut self.cur[pos..pos + 3];
830 let (ra, rg) = (cur[0] as i32, cur[2] as i32);
831 let (x, y) = (rb - rc, rb - rd);
834 let extremum = ((x ^ y) >= 0) & (x != 0) & (y != 0);
835 let e = -(extremum as i32);
836 let interp = (((rg + ra + 2 * rb) >> 2) & e) | (((ra + rg) >> 1) & !e);
837 let flat = (rb - rc).abs() + (rc - ra).abs();
838 cur[1] = st.decode_residual::<LOSSY>(grads, flat, rb - rc, rc - ra, interp);
839 }
840}
841
842#[inline(always)]
844fn even_prediction(rf: i32, rc: i32, rb: i32, rd: i32) -> i32 {
845 let (d_cb, d_fb, d_db) = ((rc - rb).abs(), (rf - rb).abs(), (rd - rb).abs());
846 let mc = -(((d_cb > d_fb) & (d_cb > d_db)) as i32);
850 let md = -(((d_db > d_cb) & (d_db > d_fb)) as i32) & !mc;
851 let dropped = (rc & mc) | (rd & md) | (rf & !(mc | md));
852 rf + rc + rd - dropped + 2 * rb
853}
854
855#[cfg(test)]
856mod tests {
857 use super::*;
858
859 #[test]
860 fn rice_parameter_matches_reference_loop() {
861 for v1 in [0, 1, 2, 3, 5, 64, 100, 1000, 65535, 1 << 20, i32::MAX / 4] {
862 for v2 in 1..=130 {
863 let mut d = 0u32;
865 if v2 < v1 {
866 loop {
867 if d > 14 {
868 break;
869 }
870 d += 1;
871 if ((v2 as i64) << d) >= v1 as i64 {
872 break;
873 }
874 }
875 }
876 assert_eq!(bit_diff(v1, v2), d, "v1 {v1} v2 {v2}");
877 }
878 }
879 }
880
881 fn header(lossless: u8, raw_type: u8, bits: u8, h: u16, rw: u16, w: u16, blocks: u8) -> Vec<u8> {
882 let mut v = vec![0x49, 0x53, lossless, raw_type, bits];
883 v.extend(h.to_be_bytes());
884 v.extend(rw.to_be_bytes());
885 v.extend(w.to_be_bytes());
886 v.extend(0x300u16.to_be_bytes());
887 v.push(blocks);
888 v.extend((h / 6).to_be_bytes());
889 v
890 }
891
892 #[test]
893 fn header_validation() {
894 let h = header(1, 16, 14, 4182, 6912, 6384, 9);
895 let p = FujiHeader::parse(&h).unwrap();
896 assert!(p.lossless && p.xtrans);
897 assert_eq!((p.width, p.height, p.blocks, p.lines), (6384, 4182, 9, 697));
898 assert_eq!(p.block_width(8), 6384 - 8 * 768);
899 assert!(FujiHeader::parse(&header(1, 16, 13, 4182, 6912, 6384, 9)).is_err());
900 assert!(FujiHeader::parse(&header(1, 16, 14, 4181, 6912, 6384, 9)).is_err());
901 assert!(FujiHeader::parse(&header(1, 16, 14, 4182, 6912, 6384, 8)).is_err());
902 assert!(FujiHeader::parse(&header(2, 16, 14, 4182, 6912, 6384, 9)).is_err());
903 assert!(FujiHeader::parse(&h[..10]).is_err());
904 }
905
906 #[test]
907 fn qtables() {
908 let t = QTable::main(0, 16383);
909 assert_eq!(t.total_values, 16384);
910 assert_eq!(t.raw_bits, 14);
911 assert_eq!(t.level(0), 0);
912 assert_eq!(t.level(1), 1);
913 assert_eq!(t.level(-1), -1);
914 assert_eq!(t.level(0x12), 2);
915 assert_eq!(t.level(16383), 4);
916 assert_eq!(t.level(-16383), -4);
917 let t = QTable::main(3, 16383);
918 assert_eq!(t.total_values, (16383 + 6) / 7 + 1);
919 let x = QTable::lossy_extra(4095);
920 assert_eq!(x[1].total_values, (4095 + 2) / 3 + 1);
921 assert_eq!(log2ceil(16384), 14);
922 assert_eq!(log2ceil(16385), 15);
923 assert_eq!(log2ceil(1), 0);
924 }
925
926 fn stream(lossless: bool, xtrans: bool, payload: &[u8]) -> Vec<u8> {
927 let mut v = header(lossless as u8, if xtrans { 16 } else { 0 }, 14, 12, 768, 768, 1);
929 v.extend((payload.len() as u32).to_be_bytes());
930 v.extend([0u8; 12]);
931 if !lossless {
932 v.extend([0u8, 2]);
933 v.extend([0u8; 14]);
934 }
935 v.extend_from_slice(payload);
936 v
937 }
938
939 fn pseudo(n: usize, seed: u64) -> Vec<u8> {
940 let mut x = seed;
941 (0..n)
942 .map(|_| {
943 x ^= x << 13;
944 x ^= x >> 7;
945 x ^= x << 17;
946 (x >> 24) as u8 | 0x11
947 })
948 .collect()
949 }
950
951 const XT: ColorTile = [
952 [1, 1, 0, 1, 1, 2],
953 [1, 1, 2, 1, 1, 0],
954 [2, 0, 1, 0, 2, 1],
955 [1, 1, 2, 1, 1, 0],
956 [1, 1, 0, 1, 1, 2],
957 [0, 2, 1, 2, 0, 1],
958 ];
959
960 const BAYER: ColorTile = [
961 [0, 1, 0, 1, 0, 1],
962 [1, 2, 1, 2, 1, 2],
963 [0, 1, 0, 1, 0, 1],
964 [1, 2, 1, 2, 1, 2],
965 [0, 1, 0, 1, 0, 1],
966 [1, 2, 1, 2, 1, 2],
967 ];
968
969 #[test]
970 fn decodes_synthetic_streams_without_panicking() {
971 for (lossless, xtrans) in [(true, true), (true, false), (false, true), (false, false)] {
972 for seed in 1..6 {
973 let data = stream(lossless, xtrans, &pseudo(200_000, seed));
974 let c = FujiCompressed::new(&data).unwrap();
975 let mut out = vec![0u16; 768 * 12];
976 let tile = if xtrans { XT } else { BAYER };
977 if c.decode(&mut out, 768, 12, &tile, false).is_ok() {
979 assert!(out.iter().all(|&v| v < 1 << 14));
980 }
981 }
982 }
983 }
984
985 #[test]
986 fn region_decode_matches_full_decode() {
987 let mut v = header(1, 16, 14, 12, 1536, 1536, 2);
989 let payload = vec![0xFFu8; 300_000];
990 v.extend((payload.len() as u32).to_be_bytes());
991 v.extend((payload.len() as u32).to_be_bytes());
992 v.extend([0u8; 8]);
993 v.extend_from_slice(&payload);
994 v.extend_from_slice(&payload);
995 let c = FujiCompressed::new(&v).unwrap();
996 let mut full = vec![0u16; 1536 * 12];
997 c.decode(&mut full, 1536, 12, &XT, true).unwrap();
998 for r in [Rect::new(700, 3, 100, 5), Rect::new(0, 0, 10, 1), Rect::new(800, 6, 736, 6)] {
999 let mut part = vec![0u16; (r.width * r.height) as usize];
1000 c.decode_region(&mut part, r, &XT, true).unwrap();
1001 for y in 0..r.height as usize {
1002 let src = &full[(r.y as usize + y) * 1536 + r.x as usize..][..r.width as usize];
1003 assert_eq!(&part[y * r.width as usize..][..r.width as usize], src, "{r:?} row {y}");
1004 }
1005 }
1006 let mut small = vec![0u16; 4];
1007 assert!(c.decode_region(&mut small, Rect::new(1530, 0, 10, 1), &XT, true).is_err());
1008 }
1009
1010 #[test]
1011 fn truncated_stream_is_an_error() {
1012 let data = stream(true, true, &[0x00; 64]);
1013 let c = FujiCompressed::new(&data).unwrap();
1014 let mut out = vec![0u16; 768 * 12];
1015 assert!(c.decode(&mut out, 768, 12, &XT, true).is_err());
1016 assert!(FujiCompressed::new(&data[..18]).is_err());
1018 }
1019
1020 #[test]
1021 fn all_ones_stream_is_deterministic() {
1022 let data = stream(true, true, &vec![0xFF; 400_000]);
1024 let c = FujiCompressed::new(&data).unwrap();
1025 let mut a = vec![0u16; 768 * 12];
1026 let mut b = vec![0u16; 768 * 12];
1027 c.decode(&mut a, 768, 12, &XT, false).unwrap();
1028 c.decode(&mut b, 768, 12, &XT, true).unwrap();
1029 assert_eq!(a, b);
1030 }
1031}