1const MAX_P: usize = 15;
23const MAX_CZ: usize = MAX_P + 3;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum PredictionMode {
29 Full,
31 Reduced,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum LocalSumType {
38 WideNeighbor,
40 NarrowNeighbor,
42 WideColumn,
44 NarrowColumn,
46}
47
48#[derive(Debug, Clone)]
50pub struct Config {
51 pub nx: u16,
53 pub ny: u16,
55 pub nz: u16,
57 pub dynamic_range: u8,
59 pub signed_samples: bool,
61 pub p: u8,
63 pub mode: PredictionMode,
65 pub local_sum_type: LocalSumType,
67 pub omega: u8,
69 pub register_size: u8,
71 pub t_inc: u16,
74 pub v_min: i8,
76 pub v_max: i8,
78 pub u_max: u8,
80 pub gamma_0: u8,
82 pub gamma_star: u8,
84 pub accum_init_k: Option<u8>,
87}
88
89#[derive(Debug, thiserror::Error)]
91pub enum Error {
92 #[error("Invalid configuration parameter")]
94 InvalidConfig,
95 #[error("Output buffer too small")]
97 OutputFull,
98 #[error("Input bitstream truncated or malformed")]
100 Truncated,
101 #[error("Image dimensions exceed compile-time limits")]
103 ImageTooLarge,
104 #[error("Scratch buffer too small")]
106 ScratchTooSmall,
107}
108
109pub fn scratch_len(nx: usize, nz: usize) -> usize {
115 nz * 2 * nx + nz * MAX_CZ + nz
119}
120
121impl Config {
122 fn validate(&self) -> Result<(), Error> {
123 if self.nx == 0 || self.ny == 0 || self.nz == 0 {
124 return Err(Error::InvalidConfig);
125 }
126 let d = self.dynamic_range;
127 if d < 2 || d > 16 {
128 return Err(Error::InvalidConfig);
129 }
130 if self.p > 15 {
131 return Err(Error::InvalidConfig);
132 }
133 if self.omega < 4 || self.omega > 19 {
134 return Err(Error::InvalidConfig);
135 }
136 let r_min = core::cmp::max(32, d as u8 + self.omega + 2);
137 if self.register_size < r_min || self.register_size > 64 {
138 return Err(Error::InvalidConfig);
139 }
140 if !self.t_inc.is_power_of_two()
141 || self.t_inc < 16
142 || self.t_inc > 2048
143 {
144 return Err(Error::InvalidConfig);
145 }
146 if self.v_min < -6 || self.v_min > 9 {
147 return Err(Error::InvalidConfig);
148 }
149 if self.v_max < self.v_min || self.v_max > 9 {
150 return Err(Error::InvalidConfig);
151 }
152 if self.u_max < 8 || self.u_max > 32 {
153 return Err(Error::InvalidConfig);
154 }
155 if self.gamma_0 < 1 || self.gamma_0 > 8 {
156 return Err(Error::InvalidConfig);
157 }
158 let g_min = core::cmp::max(4, self.gamma_0 + 1);
159 if self.gamma_star < g_min || self.gamma_star > 11 {
160 return Err(Error::InvalidConfig);
161 }
162 if self.nx == 1
163 && self.mode == PredictionMode::Full
164 {
165 return Err(Error::InvalidConfig);
166 }
167 if self.nx == 1
168 && matches!(
169 self.local_sum_type,
170 LocalSumType::WideNeighbor
171 | LocalSumType::NarrowNeighbor
172 )
173 {
174 return Err(Error::InvalidConfig);
175 }
176 Ok(())
177 }
178
179 fn d(&self) -> u32 {
180 self.dynamic_range as u32
181 }
182
183 fn s_min(&self) -> i64 {
184 if self.signed_samples {
185 -(1i64 << (self.d() - 1))
186 } else {
187 0
188 }
189 }
190
191 fn s_max(&self) -> i64 {
192 if self.signed_samples {
193 (1i64 << (self.d() - 1)) - 1
194 } else {
195 (1i64 << self.d()) - 1
196 }
197 }
198
199 fn s_mid(&self) -> i64 {
200 if self.signed_samples {
201 0
202 } else {
203 1i64 << (self.d() - 1)
204 }
205 }
206
207 fn c_z(&self, z: usize) -> usize {
209 let p_star = core::cmp::min(z, self.p as usize);
210 match self.mode {
211 PredictionMode::Reduced => p_star,
212 PredictionMode::Full => p_star + 3,
213 }
214 }
215}
216
217struct BitWriter<'a> {
220 buf: &'a mut [u8],
221 pos: usize,
222 bit: u32,
223}
224
225impl<'a> BitWriter<'a> {
226 fn new(buf: &'a mut [u8]) -> Self {
227 Self { buf, pos: 0, bit: 0 }
228 }
229
230 fn write_bits(
231 &mut self,
232 value: u64,
233 n: u32,
234 ) -> Result<(), Error> {
235 for i in (0..n).rev() {
236 let b = ((value >> i) & 1) as u8;
237 if self.pos >= self.buf.len() {
238 return Err(Error::OutputFull);
239 }
240 self.buf[self.pos] |= b << (7 - self.bit);
241 self.bit += 1;
242 if self.bit == 8 {
243 self.bit = 0;
244 self.pos += 1;
245 }
246 }
247 Ok(())
248 }
249
250 fn flush(&mut self) -> Result<(), Error> {
252 if self.bit > 0 {
253 self.bit = 0;
254 self.pos += 1;
255 }
256 Ok(())
257 }
258
259 fn bytes_written(&self) -> usize {
260 if self.bit > 0 {
261 self.pos + 1
262 } else {
263 self.pos
264 }
265 }
266}
267
268struct BitReader<'a> {
271 buf: &'a [u8],
272 pos: usize,
273 bit: u32,
274}
275
276impl<'a> BitReader<'a> {
277 fn new(buf: &'a [u8]) -> Self {
278 Self { buf, pos: 0, bit: 0 }
279 }
280
281 fn read_bits(&mut self, n: u32) -> Result<u64, Error> {
282 let mut val = 0u64;
283 for _ in 0..n {
284 if self.pos >= self.buf.len() {
285 return Err(Error::Truncated);
286 }
287 let b = (self.buf[self.pos] >> (7 - self.bit)) & 1;
288 val = (val << 1) | b as u64;
289 self.bit += 1;
290 if self.bit == 8 {
291 self.bit = 0;
292 self.pos += 1;
293 }
294 }
295 Ok(val)
296 }
297
298}
299
300fn get_sample(
304 image: &[u16],
305 nx: usize,
306 ny: usize,
307 nz: usize,
308 z: usize,
309 y: usize,
310 x: usize,
311) -> i64 {
312 let _ = (ny, nz);
313 image[z * ny * nx + y * nx + x] as i64
314}
315
316fn init_weights_default(
318 cfg: &Config,
319 z: usize,
320 w: &mut [i64],
321) {
322 let p_star = core::cmp::min(z, cfg.p as usize);
323 let omega = cfg.omega as i64;
324
325 let c_z = cfg.c_z(z);
327 for j in 0..c_z {
328 w[j] = 0;
329 }
330
331 let dir_offset = match cfg.mode {
332 PredictionMode::Full => {
333 w[0] = 0;
335 w[1] = 0;
336 w[2] = 0;
337 3
338 }
339 PredictionMode::Reduced => 0,
340 };
341
342 if p_star > 0 {
343 w[dir_offset] = (7 * (1i64 << omega)) / 8;
345 for i in 1..p_star {
346 w[dir_offset + i] = w[dir_offset + i - 1] / 8;
347 }
348 }
349}
350
351fn scaling_exponent(cfg: &Config, t: usize) -> i64 {
353 let v_min = cfg.v_min as i64;
354 let v_max = cfg.v_max as i64;
355 let t_inc = cfg.t_inc as i64;
356 let nx = cfg.nx as i64;
357 let d = cfg.d() as i64;
358 let omega = cfg.omega as i64;
359
360 let base = v_min + ((t as i64 - nx) / t_inc);
361 let clamped = if base < v_min {
362 v_min
363 } else if base > v_max {
364 v_max
365 } else {
366 base
367 };
368 clamped + d - omega
369}
370
371fn mod_star(x: i64, r: u32) -> i64 {
373 let two_r = 1i64 << r;
374 let half = 1i64 << (r - 1);
375 ((x + half).rem_euclid(two_r)) - half
376}
377
378fn clip(x: i64, x_min: i64, x_max: i64) -> i64 {
380 if x < x_min {
381 x_min
382 } else if x > x_max {
383 x_max
384 } else {
385 x
386 }
387}
388
389fn sgn_plus(x: i64) -> i64 {
391 if x >= 0 { 1 } else { -1 }
392}
393
394fn predicted_sample(s_tilde: i64) -> i64 {
396 if s_tilde >= 0 {
398 s_tilde / 2
399 } else {
400 (s_tilde - 1) / 2
401 }
402}
403
404fn mapped_quantizer_index(
406 cfg: &Config,
407 delta: i64,
408 s_hat: i64,
409 _t: usize,
410) -> u64 {
411 let s_min = cfg.s_min();
412 let s_max = cfg.s_max();
413
414 let theta = core::cmp::min(s_hat - s_min, s_max - s_hat);
416
417 if delta.abs() > theta {
419 (delta.abs() + theta) as u64
420 } else if delta >= 0 {
421 2 * delta as u64
422 } else {
423 2 * (-delta) as u64 - 1
424 }
425}
426
427fn unmap_quantizer_index(
429 cfg: &Config,
430 mapped: u64,
431 s_hat: i64,
432) -> i64 {
433 let s_min = cfg.s_min();
434 let s_max = cfg.s_max();
435 let theta =
436 core::cmp::min(s_hat - s_min, s_max - s_hat);
437
438 if mapped as i64 > 2 * theta {
439 let abs_val = mapped as i64 - theta;
440 if s_hat - s_min <= s_max - s_hat {
444 abs_val
445 } else {
446 -abs_val
447 }
448 } else if mapped % 2 == 0 {
449 (mapped / 2) as i64
450 } else {
451 -(((mapped + 1) / 2) as i64)
452 }
453}
454
455fn write_gpo2(
459 w: &mut BitWriter,
460 j: u64,
461 k: u32,
462 u_max: u32,
463 d: u32,
464) -> Result<(), Error> {
465 let quotient = j >> k;
466 if quotient < u_max as u64 {
467 for _ in 0..quotient {
469 w.write_bits(0, 1)?;
470 }
471 w.write_bits(1, 1)?;
472 if k > 0 {
473 w.write_bits(j & ((1u64 << k) - 1), k)?;
474 }
475 } else {
476 for _ in 0..u_max {
478 w.write_bits(0, 1)?;
479 }
480 w.write_bits(j, d)?;
481 }
482 Ok(())
483}
484
485fn read_gpo2(
487 r: &mut BitReader,
488 k: u32,
489 u_max: u32,
490 d: u32,
491) -> Result<u64, Error> {
492 let mut quotient = 0u64;
493 loop {
494 let bit = r.read_bits(1)?;
495 if bit == 1 {
496 break;
497 }
498 quotient += 1;
499 if quotient == u_max as u64 {
500 return r.read_bits(d);
502 }
503 }
504 let remainder = if k > 0 { r.read_bits(k)? } else { 0 };
505 Ok((quotient << k) | remainder)
506}
507
508pub fn compress(
517 cfg: &Config,
518 image: &[u16],
519 out: &mut [u8],
520 scratch: &mut [i64],
521) -> Result<usize, Error> {
522 cfg.validate()?;
523
524 let nx = cfg.nx as usize;
525 let ny = cfg.ny as usize;
526 let nz = cfg.nz as usize;
527 let d = cfg.d();
528 let needed = scratch_len(nx, nz);
529
530 if scratch.len() < needed {
531 return Err(Error::ScratchTooSmall);
532 }
533 if image.len() < nx * ny * nz {
534 return Err(Error::Truncated);
535 }
536
537 for b in out.iter_mut() {
539 *b = 0;
540 }
541 for s in scratch[..needed].iter_mut() {
542 *s = 0;
543 }
544
545 let sr_len = nz * 2 * nx;
547 let w_len = nz * MAX_CZ;
548 let (sr_buf, rest) = scratch.split_at_mut(sr_len);
549 let (weights, acc_buf) = rest.split_at_mut(w_len);
550 let acc = &mut acc_buf[..nz];
552
553 let mut bw = BitWriter::new(out);
554 write_header(cfg, &mut bw)?;
555
556 for z in 0..nz {
558 let cz = cfg.c_z(z);
559 let wbase = z * MAX_CZ;
560 let mut wslice = [0i64; MAX_CZ];
561 init_weights_default(cfg, z, &mut wslice);
562 for j in 0..cz {
563 weights[wbase + j] = wslice[j];
564 }
565 }
566
567 let mut counter = 1u64 << cfg.gamma_0;
569 for z in 0..nz {
570 let k_z_pp = match cfg.accum_init_k {
571 Some(k) => k as u32,
572 None => 0,
573 };
574 let k_z_prime = if k_z_pp <= 30 - d {
575 k_z_pp
576 } else {
577 2 * k_z_pp + d - 30
578 };
579 acc[z] = ((3 * (1u64 << (k_z_prime + 6)) - 49)
580 * counter) as i64
581 >> 7;
582 }
583
584 for z in 0..nz {
585 for y in 0..ny {
586 let cur_row = y % 2;
587 let prev_row = (y + 1) % 2;
588
589 for x in 0..nx {
590 let t = y * nx + x;
591 let s = get_sample(image, nx, ny, nz, z, y, x);
592
593 if t == 0 {
594 let coded = if cfg.signed_samples {
595 (s - cfg.s_min()) as u64
596 } else {
597 s as u64
598 };
599 bw.write_bits(coded, d)?;
600 sr_buf[z * 2 * nx + cur_row * nx + x] = s;
601 continue;
602 }
603
604 let sigma = local_sum_with_buf(
605 cfg, sr_buf, nx, z, y, x, cur_row,
606 prev_row,
607 );
608
609 let mut u_vec = [0i64; MAX_CZ];
610 let cz = cfg.c_z(z);
611 if cz > 0 {
612 build_u_with_buf(
613 cfg, sr_buf, sigma, nx, z, y, x,
614 cur_row, prev_row, &mut u_vec,
615 );
616 }
617
618 let wbase = z * MAX_CZ;
619 let mut d_hat = 0i64;
620 for j in 0..cz {
621 d_hat += weights[wbase + j] * u_vec[j];
622 }
623
624 let s_tilde = high_res_predicted_buf(
625 cfg, d_hat, sigma, t, z, sr_buf, nx,
626 );
627 let s_hat = predicted_sample(s_tilde);
628 let delta = s - s_hat;
629 let mapped =
630 mapped_quantizer_index(cfg, delta, s_hat, t);
631
632 let mut acc_u = acc[z] as u64;
633 encode_sample(
634 cfg, &mut bw, mapped, &mut acc_u,
635 &mut counter, t, z,
636 )?;
637 acc[z] = acc_u as i64;
638
639 sr_buf[z * 2 * nx + cur_row * nx + x] = s;
640
641 if cz > 0 {
643 let e = 2 * s - s_tilde;
644 let rho = scaling_exponent(cfg, t);
645 let omega_min =
646 -(1i64 << (cfg.omega as i64 + 2));
647 let omega_max =
648 (1i64 << (cfg.omega as i64 + 2)) - 1;
649
650 for j in 0..cz {
651 let inc = (sgn_plus(e)
652 * shift_right(u_vec[j], rho)
653 + 1)
654 / 2;
655 weights[wbase + j] = clip(
656 weights[wbase + j] + inc,
657 omega_min,
658 omega_max,
659 );
660 }
661 }
662 }
663 }
664 }
665
666 bw.flush()?;
667 Ok(bw.bytes_written())
668}
669
670fn shift_right(x: i64, shift: i64) -> i64 {
673 if shift >= 0 {
674 x >> shift
675 } else {
676 x << (-shift)
677 }
678}
679
680fn local_sum_with_buf(
682 cfg: &Config,
683 sr_buf: &[i64],
684 nx: usize,
685 z: usize,
686 y: usize,
687 x: usize,
688 cur_row: usize,
689 prev_row: usize,
690) -> i64 {
691 let base = z * 2 * nx;
692 let get_cur =
693 |xx: usize| -> i64 { sr_buf[base + cur_row * nx + xx] };
694 let get_prev =
695 |xx: usize| -> i64 { sr_buf[base + prev_row * nx + xx] };
696 let get_prev_band = |zz: usize, row: usize, xx: usize| -> i64 {
698 sr_buf[zz * 2 * nx + row * nx + xx]
699 };
700
701 match cfg.local_sum_type {
702 LocalSumType::WideNeighbor => {
703 if y > 0 && x > 0 && x < nx - 1 {
704 get_cur(x - 1)
705 + get_prev(x - 1)
706 + get_prev(x)
707 + get_prev(x + 1)
708 } else if y > 0 && x == 0 {
709 2 * (get_prev(x) + get_prev(x + 1))
710 } else if y > 0 && x == nx - 1 {
711 get_cur(x - 1)
712 + get_prev(x - 1)
713 + 2 * get_prev(x)
714 } else {
715 4 * get_cur(x - 1)
717 }
718 }
719 LocalSumType::NarrowNeighbor => {
720 if y > 0 && x > 0 && x < nx - 1 {
721 get_prev(x - 1)
722 + 2 * get_prev(x)
723 + get_prev(x + 1)
724 } else if y == 0 && x > 0 && z > 0 {
725 let prev_band_row = if y == 0 {
726 (cfg.ny as usize - 1) % 2
732 } else {
733 prev_row
734 };
735 4 * get_prev_band(z - 1, prev_band_row, x - 1)
736 } else if y > 0 && x == 0 {
737 2 * (get_prev(x) + get_prev(x + 1))
738 } else if y > 0 && x == nx - 1 {
739 2 * (get_prev(x - 1) + get_prev(x))
740 } else {
741 4 * cfg.s_mid()
743 }
744 }
745 LocalSumType::WideColumn => {
746 if y > 0 {
747 4 * get_prev(x)
748 } else {
749 4 * get_cur(x - 1)
751 }
752 }
753 LocalSumType::NarrowColumn => {
754 if y > 0 {
755 4 * get_prev(x)
756 } else if y == 0 && x > 0 && z > 0 {
757 let prev_band_row =
758 (cfg.ny as usize - 1) % 2;
759 4 * get_prev_band(z - 1, prev_band_row, x - 1)
760 } else {
761 4 * cfg.s_mid()
762 }
763 }
764 }
765}
766
767fn build_u_with_buf(
769 cfg: &Config,
770 sr_buf: &[i64],
771 sigma: i64,
772 nx: usize,
773 z: usize,
774 y: usize,
775 x: usize,
776 cur_row: usize,
777 prev_row: usize,
778 u: &mut [i64],
779) {
780 let p_star = core::cmp::min(z, cfg.p as usize);
781 let base = z * 2 * nx;
782 let get_cur =
783 |xx: usize| -> i64 { sr_buf[base + cur_row * nx + xx] };
784 let get_prev =
785 |xx: usize| -> i64 { sr_buf[base + prev_row * nx + xx] };
786
787 match cfg.mode {
788 PredictionMode::Full => {
789 u[0] = if y > 0 {
791 4 * get_prev(x) - sigma
792 } else {
793 0
794 };
795 u[1] = if y > 0 {
797 if x > 0 {
798 4 * get_cur(x - 1) - sigma
799 } else {
800 4 * get_prev(x) - sigma
801 }
802 } else {
803 0
804 };
805 u[2] = if y > 0 {
807 if x > 0 {
808 4 * get_prev(x - 1) - sigma
809 } else {
810 4 * get_prev(x) - sigma
811 }
812 } else {
813 0
814 };
815 for i in 1..=p_star {
817 let bz = z - i;
818 let sigma_prev = local_sum_with_buf(
821 cfg, sr_buf, nx, bz, y, x, cur_row,
822 prev_row,
823 );
824 let bbase = bz * 2 * nx;
825 let sr_val =
826 sr_buf[bbase + cur_row * nx + x];
827 u[2 + i] = 4 * sr_val - sigma_prev;
828 }
829 }
830 PredictionMode::Reduced => {
831 for i in 1..=p_star {
832 let bz = z - i;
833 let sigma_prev = local_sum_with_buf(
834 cfg, sr_buf, nx, bz, y, x, cur_row,
835 prev_row,
836 );
837 let bbase = bz * 2 * nx;
838 let sr_val =
839 sr_buf[bbase + cur_row * nx + x];
840 u[i - 1] = 4 * sr_val - sigma_prev;
841 }
842 }
843 }
844}
845
846fn high_res_predicted_buf(
848 cfg: &Config,
849 d_hat: i64,
850 sigma: i64,
851 t: usize,
852 z: usize,
853 sr_buf: &[i64],
854 nx: usize,
855) -> i64 {
856 let omega = cfg.omega as u32;
857 let r = cfg.register_size as u32;
858 let s_mid = cfg.s_mid();
859 let s_min = cfg.s_min();
860 let s_max = cfg.s_max();
861
862 if t > 0 {
863 let inner = d_hat
864 + (1i64 << omega) * (sigma - 4 * s_mid)
865 + (1i64 << (omega + 2)) * s_mid
866 + (1i64 << (omega + 1));
867 clip(
868 mod_star(inner, r),
869 (1i64 << (omega + 2)) * s_min
870 + (1i64 << (omega + 1)),
871 (1i64 << (omega + 2)) * s_max
872 + (1i64 << (omega + 1)),
873 )
874 } else if t == 0 && cfg.p > 0 && z > 0 {
875 2 * sr_buf[(z - 1) * 2 * nx]
877 } else {
878 2 * s_mid
879 }
880}
881
882fn encode_sample(
885 cfg: &Config,
886 w: &mut BitWriter,
887 mapped: u64,
888 sigma_acc: &mut u64,
889 counter: &mut u64,
890 _t: usize,
891 _z: usize,
892) -> Result<(), Error> {
893 let d = cfg.d();
894 let u_max = cfg.u_max as u32;
895 let gamma_star = cfg.gamma_star as u64;
896
897 let k = if 2 * *counter > *sigma_acc + (49 * *counter >> 7) {
899 0u32
900 } else {
901 let threshold = *sigma_acc + (49 * *counter >> 7);
904 let mut k = 0u32;
905 while k < d - 2 {
906 if *counter * (1u64 << (k + 1)) > threshold {
907 break;
908 }
909 k += 1;
910 }
911 k
912 };
913
914 write_gpo2(w, mapped, k, u_max, d)?;
916
917 if *counter < (1u64 << gamma_star) - 1 {
919 *sigma_acc += mapped;
920 *counter += 1;
921 } else {
922 *sigma_acc = (*sigma_acc + mapped + 1) / 2;
923 *counter = (*counter + 1) / 2;
924 }
925
926 Ok(())
927}
928
929fn write_header(
932 cfg: &Config,
933 w: &mut BitWriter,
934) -> Result<(), Error> {
935 let d = cfg.d();
936
937 w.write_bits(0, 8)?; w.write_bits(cfg.nx as u64, 16)?; w.write_bits(cfg.ny as u64, 16)?; w.write_bits(cfg.nz as u64, 16)?; let sample_type = if cfg.signed_samples { 1 } else { 0 };
943 w.write_bits(sample_type, 1)?; w.write_bits(0, 1)?; let large_d = if d > 16 { 1u64 } else { 0 };
946 w.write_bits(large_d, 1)?; w.write_bits(d as u64 % 16, 4)?; w.write_bits(0, 1)?; w.write_bits(0, 16)?; w.write_bits(0, 2)?; w.write_bits(1, 3)?;
953 w.write_bits(0, 2)?;
955 w.write_bits(0, 1)?; w.write_bits(0, 2)?;
958 w.write_bits(0, 2)?; w.write_bits(0, 4)?;
961
962 w.write_bits(0, 1)?; w.write_bits(0, 1)?; w.write_bits(cfg.p as u64, 4)?; let pred_mode = match cfg.mode {
967 PredictionMode::Full => 0u64,
968 PredictionMode::Reduced => 1,
969 };
970 w.write_bits(pred_mode, 1)?;
971 w.write_bits(0, 1)?; let ls = match cfg.local_sum_type {
973 LocalSumType::WideNeighbor => 0u64,
974 LocalSumType::NarrowNeighbor => 1,
975 LocalSumType::WideColumn => 2,
976 LocalSumType::NarrowColumn => 3,
977 };
978 w.write_bits(ls, 2)?;
979 let r_enc = cfg.register_size as u64 % 64;
980 w.write_bits(r_enc, 6)?;
981 let omega_enc = (cfg.omega - 4) as u64;
982 w.write_bits(omega_enc, 4)?;
983 let t_inc_log = {
984 let mut v = cfg.t_inc;
985 let mut l = 0u32;
986 while v > 1 {
987 v >>= 1;
988 l += 1;
989 }
990 l
991 };
992 w.write_bits((t_inc_log - 4) as u64, 4)?;
993 w.write_bits((cfg.v_min + 6) as u64, 4)?;
994 w.write_bits((cfg.v_max + 6) as u64, 4)?;
995 w.write_bits(0, 1)?; w.write_bits(0, 1)?; w.write_bits(0, 1)?; w.write_bits(0, 5)?; w.write_bits(cfg.u_max as u64 % 32, 5)?;
1002 let gamma_star_enc = (cfg.gamma_star - 4) as u64;
1003 w.write_bits(gamma_star_enc, 3)?;
1004 w.write_bits(cfg.gamma_0 as u64 % 8, 3)?;
1005 match cfg.accum_init_k {
1006 Some(k) => {
1007 w.write_bits(k as u64, 4)?;
1008 w.write_bits(0, 1)?; }
1010 None => {
1011 w.write_bits(0b1111, 4)?; w.write_bits(0, 1)?; }
1014 }
1015
1016 Ok(())
1017}
1018
1019pub fn decompress(
1029 data: &[u8],
1030 image: &mut [u16],
1031 scratch: &mut [i64],
1032) -> Result<(Config, usize), Error> {
1033 let mut br = BitReader::new(data);
1034 let cfg = read_header(&mut br)?;
1035
1036 let nx = cfg.nx as usize;
1037 let ny = cfg.ny as usize;
1038 let nz = cfg.nz as usize;
1039 let d = cfg.d();
1040 let needed = scratch_len(nx, nz);
1041
1042 if scratch.len() < needed {
1043 return Err(Error::ScratchTooSmall);
1044 }
1045 if image.len() < nx * ny * nz {
1046 return Err(Error::OutputFull);
1047 }
1048
1049 for s in scratch[..needed].iter_mut() {
1050 *s = 0;
1051 }
1052
1053 let sr_len = nz * 2 * nx;
1054 let w_len = nz * MAX_CZ;
1055 let (sr_buf, rest) = scratch.split_at_mut(sr_len);
1056 let (weights, acc_buf) = rest.split_at_mut(w_len);
1057 let acc = &mut acc_buf[..nz];
1058
1059 for z in 0..nz {
1061 let cz = cfg.c_z(z);
1062 let wbase = z * MAX_CZ;
1063 let mut wslice = [0i64; MAX_CZ];
1064 init_weights_default(&cfg, z, &mut wslice);
1065 for j in 0..cz {
1066 weights[wbase + j] = wslice[j];
1067 }
1068 }
1069
1070 let mut counter = 1u64 << cfg.gamma_0;
1072 for z in 0..nz {
1073 let k_z_pp = match cfg.accum_init_k {
1074 Some(k) => k as u32,
1075 None => 0,
1076 };
1077 let k_z_prime = if k_z_pp <= 30 - d {
1078 k_z_pp
1079 } else {
1080 2 * k_z_pp + d - 30
1081 };
1082 acc[z] = ((3 * (1u64 << (k_z_prime + 6)) - 49)
1083 * counter) as i64
1084 >> 7;
1085 }
1086
1087 for z in 0..nz {
1088 for y in 0..ny {
1089 let cur_row = y % 2;
1090 let prev_row = (y + 1) % 2;
1091
1092 for x in 0..nx {
1093 let t = y * nx + x;
1094
1095 if t == 0 {
1096 let raw = br.read_bits(d)? as i64;
1097 let s = if cfg.signed_samples {
1098 raw + cfg.s_min()
1099 } else {
1100 raw
1101 };
1102 image[z * ny * nx + y * nx + x] = s as u16;
1103 sr_buf[z * 2 * nx + cur_row * nx + x] = s;
1104 continue;
1105 }
1106
1107 let sigma = local_sum_with_buf(
1108 &cfg, sr_buf, nx, z, y, x, cur_row,
1109 prev_row,
1110 );
1111
1112 let mut u_vec = [0i64; MAX_CZ];
1113 let cz = cfg.c_z(z);
1114 if cz > 0 {
1115 build_u_with_buf(
1116 &cfg, sr_buf, sigma, nx, z, y, x,
1117 cur_row, prev_row, &mut u_vec,
1118 );
1119 }
1120
1121 let wbase = z * MAX_CZ;
1122 let mut d_hat = 0i64;
1123 for j in 0..cz {
1124 d_hat += weights[wbase + j] * u_vec[j];
1125 }
1126
1127 let s_tilde = high_res_predicted_buf(
1128 &cfg, d_hat, sigma, t, z, sr_buf, nx,
1129 );
1130 let s_hat = predicted_sample(s_tilde);
1131
1132 let mut acc_u = acc[z] as u64;
1133 let mapped = decode_sample(
1134 &cfg, &mut br, &mut acc_u,
1135 &mut counter, t, z,
1136 )?;
1137 acc[z] = acc_u as i64;
1138
1139 let delta =
1140 unmap_quantizer_index(&cfg, mapped, s_hat);
1141 let s = s_hat + delta;
1142
1143 image[z * ny * nx + y * nx + x] = s as u16;
1144 sr_buf[z * 2 * nx + cur_row * nx + x] = s;
1145
1146 if cz > 0 {
1147 let e = 2 * s - s_tilde;
1148 let rho = scaling_exponent(&cfg, t);
1149 let omega_min =
1150 -(1i64 << (cfg.omega as i64 + 2));
1151 let omega_max =
1152 (1i64 << (cfg.omega as i64 + 2)) - 1;
1153
1154 for j in 0..cz {
1155 let inc = (sgn_plus(e)
1156 * shift_right(u_vec[j], rho)
1157 + 1)
1158 / 2;
1159 weights[wbase + j] = clip(
1160 weights[wbase + j] + inc,
1161 omega_min,
1162 omega_max,
1163 );
1164 }
1165 }
1166 }
1167 }
1168 }
1169
1170 Ok((cfg, nx * ny * nz))
1171}
1172
1173fn decode_sample(
1175 cfg: &Config,
1176 r: &mut BitReader,
1177 sigma_acc: &mut u64,
1178 counter: &mut u64,
1179 _t: usize,
1180 _z: usize,
1181) -> Result<u64, Error> {
1182 let d = cfg.d();
1183 let u_max = cfg.u_max as u32;
1184 let gamma_star = cfg.gamma_star as u64;
1185
1186 let k = if 2 * *counter > *sigma_acc + (49 * *counter >> 7) {
1187 0u32
1188 } else {
1189 let threshold = *sigma_acc + (49 * *counter >> 7);
1190 let mut k = 0u32;
1191 while k < d - 2 {
1192 if *counter * (1u64 << (k + 1)) > threshold {
1193 break;
1194 }
1195 k += 1;
1196 }
1197 k
1198 };
1199
1200 let mapped = read_gpo2(r, k, u_max, d)?;
1201
1202 if *counter < (1u64 << gamma_star) - 1 {
1204 *sigma_acc += mapped;
1205 *counter += 1;
1206 } else {
1207 *sigma_acc = (*sigma_acc + mapped + 1) / 2;
1208 *counter = (*counter + 1) / 2;
1209 }
1210
1211 Ok(mapped)
1212}
1213
1214fn read_header(r: &mut BitReader) -> Result<Config, Error> {
1215 let _user_data = r.read_bits(8)?;
1217 let nx = r.read_bits(16)? as u16;
1218 let ny = r.read_bits(16)? as u16;
1219 let nz = r.read_bits(16)? as u16;
1220 let signed_samples = r.read_bits(1)? == 1;
1221 let _reserved = r.read_bits(1)?;
1222 let large_d = r.read_bits(1)?;
1223 let d_enc = r.read_bits(4)? as u8;
1224 let d = if large_d == 1 {
1225 d_enc as u8 + 16
1226 } else if d_enc == 0 {
1227 16
1228 } else {
1229 d_enc
1230 };
1231 let _encoding_order = r.read_bits(1)?; let _sub_frame_depth = r.read_bits(16)?;
1233 let _reserved = r.read_bits(2)?;
1234 let b_enc = r.read_bits(3)?;
1235 let _output_word_size = if b_enc == 0 { 8 } else { b_enc };
1236 let entropy_type = r.read_bits(2)?;
1237 if entropy_type != 0 {
1238 return Err(Error::InvalidConfig); }
1240 let _reserved = r.read_bits(1)?;
1241 let fidelity = r.read_bits(2)?;
1242 if fidelity != 0 {
1243 return Err(Error::InvalidConfig); }
1245 let _reserved = r.read_bits(2)?;
1246 let _tau = r.read_bits(4)?;
1247
1248 let _reserved = r.read_bits(1)?;
1250 let _sr_flag = r.read_bits(1)?;
1251 let p = r.read_bits(4)? as u8;
1252 let pred_mode_bit = r.read_bits(1)?;
1253 let mode = if pred_mode_bit == 0 {
1254 PredictionMode::Full
1255 } else {
1256 PredictionMode::Reduced
1257 };
1258 let _weo_flag = r.read_bits(1)?;
1259 let ls_enc = r.read_bits(2)?;
1260 let local_sum_type = match ls_enc {
1261 0 => LocalSumType::WideNeighbor,
1262 1 => LocalSumType::NarrowNeighbor,
1263 2 => LocalSumType::WideColumn,
1264 _ => LocalSumType::NarrowColumn,
1265 };
1266 let r_enc = r.read_bits(6)? as u8;
1267 let register_size = if r_enc == 0 { 64 } else { r_enc };
1268 let omega = r.read_bits(4)? as u8 + 4;
1269 let t_inc_enc = r.read_bits(4)? as u32;
1270 let t_inc = 1u16 << (t_inc_enc + 4);
1271 let v_min = r.read_bits(4)? as i8 - 6;
1272 let v_max = r.read_bits(4)? as i8 - 6;
1273 let _weo_table_flag = r.read_bits(1)?;
1274 let _weight_init_method = r.read_bits(1)?;
1275 let _weight_init_table = r.read_bits(1)?;
1276 let _weight_init_res = r.read_bits(5)?;
1277
1278 let u_max_enc = r.read_bits(5)? as u8;
1280 let u_max = if u_max_enc == 0 { 32 } else { u_max_enc };
1281 let gamma_star = r.read_bits(3)? as u8 + 4;
1282 let gamma_0_enc = r.read_bits(3)? as u8;
1283 let gamma_0 = if gamma_0_enc == 0 { 8 } else { gamma_0_enc };
1284 let k_enc = r.read_bits(4)? as u8;
1285 let accum_init_k =
1286 if k_enc == 0b1111 { None } else { Some(k_enc) };
1287 let _table_flag = r.read_bits(1)?;
1288
1289 Ok(Config {
1290 nx,
1291 ny,
1292 nz,
1293 dynamic_range: d,
1294 signed_samples,
1295 p,
1296 mode,
1297 local_sum_type,
1298 omega,
1299 register_size,
1300 t_inc,
1301 v_min,
1302 v_max,
1303 u_max,
1304 gamma_0,
1305 gamma_star,
1306 accum_init_k,
1307 })
1308}
1309
1310#[cfg(test)]
1313mod tests {
1314 use super::*;
1315
1316 fn default_config(
1317 nx: u16,
1318 ny: u16,
1319 nz: u16,
1320 ) -> Config {
1321 Config {
1322 nx,
1323 ny,
1324 nz,
1325 dynamic_range: 8,
1326 signed_samples: false,
1327 p: core::cmp::min(nz.saturating_sub(1), 3) as u8,
1328 mode: if nx > 1 {
1329 PredictionMode::Full
1330 } else {
1331 PredictionMode::Reduced
1332 },
1333 local_sum_type: if nx > 1 {
1334 LocalSumType::WideNeighbor
1335 } else {
1336 LocalSumType::WideColumn
1337 },
1338 omega: 8,
1339 register_size: 32,
1340 t_inc: 64,
1341 v_min: -1,
1342 v_max: 3,
1343 u_max: 16,
1344 gamma_0: 3,
1345 gamma_star: 6,
1346 accum_init_k: None,
1347 }
1348 }
1349
1350 const SCRATCH: usize = 256;
1353
1354 fn roundtrip(cfg: &Config, image: &[u16]) {
1355 let mut compressed = [0u8; 2048];
1356 let mut scratch = [0i64; SCRATCH];
1357
1358 let n = compress(
1359 cfg, image, &mut compressed, &mut scratch,
1360 )
1361 .unwrap();
1362 assert!(n > 0);
1363
1364 let mut decoded = [0u16; 128];
1365 let n_samples = image.len();
1366 for s in scratch.iter_mut() {
1367 *s = 0;
1368 }
1369 let (_, count) = decompress(
1370 &compressed[..n],
1371 &mut decoded[..n_samples],
1372 &mut scratch,
1373 )
1374 .unwrap();
1375 assert_eq!(count, n_samples);
1376 assert_eq!(&decoded[..n_samples], image);
1377 }
1378
1379 #[test]
1380 fn roundtrip_constant() {
1381 let cfg = default_config(4, 4, 1);
1382 roundtrip(&cfg, &[128u16; 16]);
1383 }
1384
1385 #[test]
1386 fn roundtrip_ramp() {
1387 let cfg = default_config(4, 4, 1);
1388 let mut image = [0u16; 16];
1389 for i in 0..16 {
1390 image[i] = (i * 16) as u16;
1391 }
1392 roundtrip(&cfg, &image);
1393 }
1394
1395 #[test]
1396 fn roundtrip_multiband() {
1397 let cfg = default_config(4, 4, 3);
1398 let mut image = [0u16; 48];
1399 for z in 0..3 {
1400 for i in 0..16 {
1401 image[z * 16 + i] =
1402 ((z * 50 + i * 10) % 256) as u16;
1403 }
1404 }
1405 roundtrip(&cfg, &image);
1406 }
1407
1408 #[test]
1409 fn roundtrip_reduced_mode() {
1410 let cfg = Config {
1411 mode: PredictionMode::Reduced,
1412 ..default_config(4, 4, 2)
1413 };
1414 let mut image = [0u16; 32];
1415 for i in 0..32 {
1416 image[i] = (i * 7 % 256) as u16;
1417 }
1418 roundtrip(&cfg, &image);
1419 }
1420
1421 #[test]
1422 fn roundtrip_16bit() {
1423 let cfg = Config {
1424 dynamic_range: 16,
1425 register_size: 32,
1426 ..default_config(4, 4, 1)
1427 };
1428 let mut image = [0u16; 16];
1429 for i in 0..16 {
1430 image[i] = (i as u16 * 4000) + 100;
1431 }
1432 roundtrip(&cfg, &image);
1433 }
1434
1435 #[test]
1436 fn roundtrip_column_oriented() {
1437 let cfg = Config {
1438 local_sum_type: LocalSumType::WideColumn,
1439 ..default_config(4, 4, 2)
1440 };
1441 let mut image = [0u16; 32];
1442 for i in 0..32 {
1443 image[i] = (i * 13 % 256) as u16;
1444 }
1445 roundtrip(&cfg, &image);
1446 }
1447
1448 #[test]
1449 fn mapped_index_roundtrip() {
1450 let cfg = default_config(4, 4, 1);
1451 for s_hat in [0i64, 50, 127, 200, 255] {
1452 for delta in [-50i64, -1, 0, 1, 50] {
1453 let s = s_hat + delta;
1454 if s < cfg.s_min() || s > cfg.s_max() {
1455 continue;
1456 }
1457 let mapped = mapped_quantizer_index(
1458 &cfg, delta, s_hat, 1,
1459 );
1460 let recovered = unmap_quantizer_index(
1461 &cfg, mapped, s_hat,
1462 );
1463 assert_eq!(
1464 recovered, delta,
1465 "s_hat={s_hat} delta={delta} \
1466 mapped={mapped}"
1467 );
1468 }
1469 }
1470 }
1471
1472 #[test]
1473 fn gpo2_roundtrip() {
1474 let mut buf = [0u8; 64];
1475 for k in 0..4u32 {
1476 for j in 0u64..20 {
1477 for b in buf.iter_mut() {
1478 *b = 0;
1479 }
1480 let mut w = BitWriter::new(&mut buf);
1481 write_gpo2(&mut w, j, k, 16, 8).unwrap();
1482 let mut r = BitReader::new(&buf);
1483 let decoded =
1484 read_gpo2(&mut r, k, 16, 8).unwrap();
1485 assert_eq!(decoded, j, "k={k} j={j}");
1486 }
1487 }
1488 }
1489
1490 #[test]
1491 fn header_roundtrip() {
1492 let cfg = default_config(8, 8, 4);
1493 let mut buf = [0u8; 64];
1494 let mut w = BitWriter::new(&mut buf);
1495 write_header(&cfg, &mut w).unwrap();
1496
1497 let mut r = BitReader::new(&buf);
1498 let decoded = read_header(&mut r).unwrap();
1499
1500 assert_eq!(decoded.nx, cfg.nx);
1501 assert_eq!(decoded.ny, cfg.ny);
1502 assert_eq!(decoded.nz, cfg.nz);
1503 assert_eq!(decoded.dynamic_range, cfg.dynamic_range);
1504 assert_eq!(decoded.p, cfg.p);
1505 assert_eq!(decoded.omega, cfg.omega);
1506 assert_eq!(decoded.v_min, cfg.v_min);
1507 assert_eq!(decoded.v_max, cfg.v_max);
1508 assert_eq!(decoded.gamma_0, cfg.gamma_0);
1509 assert_eq!(decoded.gamma_star, cfg.gamma_star);
1510 }
1511
1512 #[test]
1513 fn single_band_no_prediction() {
1514 let cfg = Config {
1515 p: 0,
1516 mode: PredictionMode::Reduced,
1517 local_sum_type: LocalSumType::WideColumn,
1518 ..default_config(4, 4, 1)
1519 };
1520 roundtrip(&cfg, &[100u16; 16]);
1521 }
1522}