Skip to main content

flashraw_codecs/
olympus.rs

1//! Olympus / OM System ORF lossless compression.
2//!
3//! A single MSB-first bit stream codes every sample of the frame as an adaptive
4//! Golomb-like residual. Even and odd columns form two interleaved channels: each
5//! channel keeps its own adaptation state (previous code magnitude, a run counter
6//! of small codes and a smoothed residual), and predicts from same-colour
7//! neighbours two pixels to the left (`W`), above (`N`) and diagonal (`NW`).
8//!
9//! Per sample:
10//! 1. sign bit, then `low_bits` (usually 2) literal low bits, then `extra_bits`
11//!    (usually 0) bits appended below the reconstructed value;
12//! 2. a unary-coded "high" part (number of zeros before a one, escape after
13//!    `max_high` zeros to a fixed-width field), then `wbits` "low" bits, where
14//!    `wbits` adapts to the magnitude of the previous same-channel code;
15//! 3. the code is combined with the smoothed residual and added to the
16//!    edge-sensitive median prediction.
17//!
18//! The camera describes the coder with tags 0x640..0x653 of the makernote
19//! ImageProcessing directory ([`OlympusParams`]); every body to date uses the same
20//! values, which are also the defaults. The stream is inherently sequential.
21
22use flashraw_core::bits::{BitPumpMsb, BitRead};
23use flashraw_core::{Error, Result};
24
25/// Coder parameters (makernote ImageProcessing tags 0x640..0x653).
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct OlympusParams {
28    /// 0x640: extra literal low bits per sample (value is shifted left by this).
29    pub extra_bits: u32,
30    /// 0x641: log2 of the gradient threshold of the predictor.
31    pub grad_shift: u32,
32    /// 0x642: prediction used outside the image (first rows/columns).
33    pub edge_pred: i32,
34    /// 0x643: literal low bits coded before the adaptive part (0, 1 or 2).
35    pub low_bits: u32,
36    /// 0x644: smoothing shift of the residual accumulator (15 disables it).
37    pub smooth_shift: u32,
38    /// 0x645: codes up to `1 << small_shift` count as "small" for the run counter.
39    pub small_shift: u32,
40    /// 0x646: run length of small codes that switches to the fine width model.
41    pub run_switch: i32,
42    /// 0x647/0x648: width model after a run (offset, magnitude threshold).
43    pub run_offset: i32,
44    pub run_threshold: i32,
45    /// 0x649/0x650: default width model (offset, magnitude threshold).
46    pub base_offset: i32,
47    pub base_threshold: i32,
48    /// 0x651: maximum adaptive width.
49    pub max_wbits: i32,
50    /// 0x652: unary escape length.
51    pub max_high: u32,
52    /// 0x653: length of the big-endian stream header (value 1) preceding two zero bytes.
53    pub header_len: u32,
54}
55
56impl Default for OlympusParams {
57    fn default() -> Self {
58        OlympusParams {
59            extra_bits: 0,
60            grad_shift: 5,
61            edge_pred: 0,
62            low_bits: 2,
63            smooth_shift: 5,
64            small_shift: 4,
65            run_switch: 3,
66            run_offset: 2,
67            run_threshold: 3,
68            base_offset: 4,
69            base_threshold: 7,
70            max_wbits: 15,
71            max_high: 12,
72            header_len: 5,
73        }
74    }
75}
76
77impl OlympusParams {
78    /// Build from the 14 tag values 0x640..=0x653 (`None` = default).
79    pub fn from_tags(t: &[Option<u32>; 14]) -> Self {
80        let d = Self::default();
81        let g = |i: usize, def: u32| t[i].unwrap_or(def);
82        OlympusParams {
83            extra_bits: g(0, d.extra_bits),
84            grad_shift: g(1, d.grad_shift),
85            edge_pred: g(2, d.edge_pred as u32) as i32,
86            low_bits: g(3, d.low_bits),
87            smooth_shift: g(4, d.smooth_shift),
88            small_shift: g(5, d.small_shift),
89            run_switch: g(6, d.run_switch as u32) as i32,
90            run_offset: g(7, d.run_offset as u32) as i32,
91            run_threshold: g(8, d.run_threshold as u32) as i32,
92            base_offset: g(9, d.base_offset as u32) as i32,
93            base_threshold: g(10, d.base_threshold as u32) as i32,
94            max_wbits: g(11, d.max_wbits as u32) as i32,
95            max_high: g(12, d.max_high).max(1),
96            header_len: g(13, d.header_len),
97        }
98    }
99
100    fn validate(&self) -> Result<()> {
101        let ok = self.extra_bits <= 4
102            && self.grad_shift <= 16
103            && self.low_bits <= 2
104            && self.small_shift <= 31
105            && self.max_high <= 24
106            && (1..=8).contains(&self.header_len)
107            && (0..=15).contains(&self.max_wbits);
108        if ok { Ok(()) } else { Err(Error::unsupported_compression("ORF", format!("coder parameters {self:?}"))) }
109    }
110}
111
112/// Offset of the bit stream after the header, checking the header contents.
113pub fn stream_start(data: &[u8], p: &OlympusParams) -> Result<usize> {
114    let n = p.header_len as usize;
115    let hdr = data.get(..n + 2).ok_or_else(|| Error::corrupt("ORF: compressed data too short"))?;
116    let v = hdr[..n].iter().fold(0u64, |a, &b| a << 8 | b as u64);
117    if v != 1 || hdr[n] != 0 || hdr[n + 1] != 0 {
118        return Err(Error::corrupt("ORF: invalid compressed stream header"));
119    }
120    Ok(n + 2)
121}
122
123/// Per-channel (even/odd column) adaptation state.
124#[derive(Clone, Copy, Default)]
125struct Chan {
126    /// Previous code magnitude (`glc`).
127    prev_code: u32,
128    /// Run counter of small codes.
129    run: i32,
130    /// Smoothed residual (`vbits >> smooth_shift` of the previous sample).
131    smooth: i32,
132    /// Previous reconstructed value of this channel (unshifted, untruncated).
133    west: i32,
134}
135
136/// Decode a full `width x height` frame into `out` (row-major, `width` stride).
137/// `data` starts at the stream header. Returns an error on malformed parameters or
138/// a stream that runs out of data.
139pub fn decode(data: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) -> Result<()> {
140    decode_with(data, p, out, width, height, false)
141}
142
143/// [`decode`], optionally overlapping entropy decoding and prediction on two threads.
144pub fn decode_with(
145    data: &[u8],
146    p: &OlympusParams,
147    out: &mut [u16],
148    width: usize,
149    height: usize,
150    parallel: bool,
151) -> Result<()> {
152    p.validate()?;
153    if out.len() < width * height {
154        return Err(Error::InvalidArgument("ORF: output buffer too small".into()));
155    }
156    let start = stream_start(data, p)?;
157    let standard = OlympusParams { header_len: p.header_len, ..OlympusParams::default() };
158    if *p == standard {
159        decode_standard(&data[start..], out, width, height, parallel)
160    } else {
161        decode_generic(&data[start..], p, out, width, height)
162    }
163}
164
165/// The coder with the parameters every camera uses, with all of them folded into
166/// constants. Each sample needs at most 31 bits, so one 32-bit peek after a refill
167/// serves the whole code.
168///
169/// The bit stream is sequential, but its parsing never depends on reconstructed
170/// pixel values: only the prediction does. Decoding therefore runs as two stages —
171/// entropy decoding of residual codes for a band of rows, and prediction of a band
172/// (two rows at a time, i.e. four independent dependency chains) — which overlap
173/// on two threads when `parallel` is set.
174fn decode_standard(stream: &[u8], out: &mut [u16], width: usize, height: usize, parallel: bool) -> Result<()> {
175    const BAND: usize = 32;
176    let mut bits = PosBits::new(stream);
177    let mut ch = [EntropyChan::default(); 2];
178    let mut entropy_band = |y0: usize, codes: &mut Vec<i32>| -> Result<()> {
179        let y1 = (y0 + BAND).min(height);
180        codes.clear();
181        codes.resize((y1 - y0) * width, 0);
182        for (i, row) in codes.chunks_exact_mut(width).enumerate() {
183            standard_entropy_row(&mut bits, &mut ch, row);
184            if bits.exhausted() {
185                return Err(Error::corrupt(format!("ORF: compressed data ends at row {}/{height}", y0 + i)));
186            }
187        }
188        Ok(())
189    };
190    if width == 0 || height == 0 {
191        return Ok(());
192    }
193    // WASM threads cannot `std::thread::spawn` (the threaded build traps with "failed to spawn
194    // thread"), so there the two stages overlap through `rayon::join` instead: entropy-decode band
195    // k while predicting band k-1. `join` degrades to sequential execution on a one-thread pool.
196    #[cfg(all(feature = "rayon", target_arch = "wasm32"))]
197    if parallel && height > BAND {
198        let mut cur = Vec::new();
199        let mut prev: Option<(usize, Vec<i32>)> = None;
200        for y0 in (0..height).step_by(BAND) {
201            let (res, done) = rayon::join(
202                || entropy_band(y0, &mut cur).map(|_| ()),
203                || {
204                    if let Some((py, codes)) = prev.take() {
205                        predict_band(out, &codes, py, width);
206                        Some(codes)
207                    } else {
208                        None
209                    }
210                },
211            );
212            res?;
213            let recycled = done.unwrap_or_default();
214            prev = Some((y0, std::mem::replace(&mut cur, recycled)));
215        }
216        if let Some((py, codes)) = prev {
217            predict_band(out, &codes, py, width);
218        }
219        return Ok(());
220    }
221    // Two OS threads; `std::thread` cannot spawn on wasm32 (its threads are rayon's pool).
222    #[cfg(all(feature = "rayon", not(target_arch = "wasm32")))]
223    if parallel && height > BAND {
224        use std::sync::mpsc;
225        return std::thread::scope(|s| {
226            let (tx, rx) = mpsc::sync_channel::<(usize, Vec<i32>)>(4);
227            let (back_tx, back_rx) = mpsc::channel::<Vec<i32>>();
228            let consumer = s.spawn(move || {
229                for (y0, codes) in rx {
230                    predict_band(out, &codes, y0, width);
231                    let _ = back_tx.send(codes);
232                }
233            });
234            let mut result = Ok(());
235            for y0 in (0..height).step_by(BAND) {
236                let mut buf = back_rx.try_recv().unwrap_or_default();
237                if let Err(e) = entropy_band(y0, &mut buf) {
238                    result = Err(e);
239                    break;
240                }
241                if tx.send((y0, buf)).is_err() {
242                    break;
243                }
244            }
245            drop(tx);
246            let _ = consumer.join();
247            result
248        });
249    }
250    let _ = parallel;
251    let mut buf = Vec::new();
252    for y0 in (0..height).step_by(BAND) {
253        entropy_band(y0, &mut buf)?;
254        predict_band(out, &buf, y0, width);
255    }
256    Ok(())
257}
258
259/// Bit reader addressed by absolute bit position: every peek is one unaligned
260/// big-endian 8-byte load, so there is no refill bookkeeping on the critical path
261/// (which is one data-dependent bit count per sample).
262struct PosBits<'a> {
263    data: &'a [u8],
264    pos: usize,
265}
266
267impl<'a> PosBits<'a> {
268    fn new(data: &'a [u8]) -> Self {
269        PosBits { data, pos: 0 }
270    }
271
272    /// Next 32 bits (zeros past the end of the data).
273    #[inline(always)]
274    fn peek32(&self) -> u32 {
275        let byte = self.pos >> 3;
276        let w = match self.data.get(byte..byte + 8) {
277            Some(b) => u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
278            None => self.peek_tail(byte),
279        };
280        ((w << (self.pos & 7)) >> 32) as u32
281    }
282
283    #[cold]
284    fn peek_tail(&self, byte: usize) -> u64 {
285        let mut b = [0u8; 8];
286        if let Some(t) = self.data.get(byte..) {
287            b[..t.len().min(8)].copy_from_slice(&t[..t.len().min(8)]);
288        }
289        u64::from_be_bytes(b)
290    }
291
292    #[inline(always)]
293    fn skip(&mut self, n: u32) {
294        self.pos += n as usize;
295    }
296
297    /// True once more than 8 bytes past the end of the data were consumed.
298    fn exhausted(&self) -> bool {
299        self.pos > (self.data.len() + 8) * 8
300    }
301}
302
303/// Entropy-side adaptation state of one channel (even or odd columns).
304#[derive(Clone, Copy, Default)]
305struct EntropyChan {
306    prev_code: u32,
307    run: i32,
308    smooth: i32,
309}
310
311/// Decode the residual codes of one row.
312#[inline(always)]
313fn standard_entropy_row(bits: &mut PosBits, ch: &mut [EntropyChan; 2], codes: &mut [i32]) {
314    let width = codes.len();
315    let [c0, c1] = ch;
316    codes[0] = standard_code::<true>(bits, c0);
317    if width > 1 {
318        codes[1] = standard_code::<true>(bits, c1);
319    }
320    let mut col = 2;
321    while col + 1 < width {
322        codes[col] = standard_code::<false>(bits, c0);
323        codes[col + 1] = standard_code::<false>(bits, c1);
324        col += 2;
325    }
326    if col < width {
327        codes[col] = standard_code::<false>(bits, c0);
328    }
329}
330
331/// One residual code with the standard parameters. `EDGE`: first two columns.
332#[inline(always)]
333fn standard_code<const EDGE: bool>(bits: &mut PosBits, c: &mut EntropyChan) -> i32 {
334    let wbits = if EDGE {
335        c.run = 0;
336        4
337    } else {
338        let run = if c.prev_code <= 16 { c.run + 1 } else { 0 };
339        c.run = run;
340        let hb = 32 - (c.prev_code & 0xffff).leading_zeros();
341        let w = if run >= 3 { hb.max(2) } else { hb.saturating_sub(2).max(4) };
342        w.min(15)
343    };
344    let v = bits.peek32();
345    let sign = v >> 31;
346    let low2 = ((v >> 29) & 3) as i32;
347    let rest = v << 3;
348    let (high, low, used) = if rest >> 20 != 0 {
349        let z = rest.leading_zeros();
350        let after = rest << (z + 1);
351        (z, after >> (32 - wbits), 4 + z + wbits)
352    } else {
353        // Escape: 12 zeros, a (15 - wbits)-bit high part, one spare bit.
354        let after = rest << 12;
355        let hn = 15 - wbits;
356        let high = if hn == 0 { 0 } else { after >> (32 - hn) };
357        let after2 = (after << hn) << 1;
358        (high, after2 >> (32 - wbits), 31)
359    };
360    bits.skip(used);
361    let glc = low | (high << wbits);
362    c.prev_code = glc;
363    let code = (glc ^ sign.wrapping_neg()) as i32;
364    let s = if EDGE { 0 } else { c.smooth };
365    let code = low2 + 4 * (s + code);
366    let vbits = ((code >> 1) & !1) + s + (code >> 2);
367    c.smooth = vbits >> 5;
368    code
369}
370
371/// Reconstruct the rows `y0..` of a band from their residual codes.
372fn predict_band(out: &mut [u16], codes: &[i32], y0: usize, width: usize) {
373    let rows = codes.len() / width;
374    let mut i = 0;
375    while i < rows {
376        let y = y0 + i;
377        if i + 1 < rows && y >= 2 {
378            // Rows y and y+1 depend on y-2 and y-1 only: interleave them.
379            let (above, cur) = out.split_at_mut(y * width);
380            let (r0, r1) = cur[..2 * width].split_at_mut(width);
381            let up0 = &above[(y - 2) * width..(y - 1) * width];
382            let up1 = &above[(y - 1) * width..y * width];
383            predict_two_rows(
384                &codes[i * width..(i + 1) * width],
385                &codes[(i + 1) * width..(i + 2) * width],
386                r0,
387                r1,
388                up0,
389                up1,
390            );
391            i += 2;
392        } else {
393            let (above, cur) = out.split_at_mut(y * width);
394            let up = if y >= 2 { &above[(y - 2) * width..(y - 1) * width] } else { &[][..] };
395            predict_row(&codes[i * width..(i + 1) * width], &mut cur[..width], up);
396            i += 1;
397        }
398    }
399}
400
401/// Predict one row (`up` empty for the first two rows of the frame).
402fn predict_row(codes: &[i32], cur: &mut [u16], up: &[u16]) {
403    let width = cur.len();
404    let mut west = [0i32; 2];
405    for col in 0..width {
406        let (n, nw) = match (up.get(col), col >= 2) {
407            (Some(&n), true) => (n as i32, up[col - 2] as i32),
408            (Some(&n), false) => (n as i32, 0),
409            (None, _) => (0, 0),
410        };
411        let w = if col >= 2 { west[col & 1] } else { 0 };
412        let v = codes[col].wrapping_add(predict(w, n, nw, 32));
413        west[col & 1] = v;
414        cur[col] = v as u16;
415    }
416}
417
418/// Predict two consecutive rows with four interleaved dependency chains.
419#[inline(never)]
420fn predict_two_rows(c0: &[i32], c1: &[i32], r0: &mut [u16], r1: &mut [u16], u0: &[u16], u1: &[u16]) {
421    let width = r0.len();
422    if width < 4 {
423        predict_row(c0, r0, u0);
424        predict_row(c1, r1, u1);
425        return;
426    }
427    // Columns 0 and 1: west and north-west are outside the image.
428    let mut w = [0i32; 4];
429    for k in 0..2 {
430        let a = c0[k].wrapping_add(predict(0, u0[k] as i32, 0, 32));
431        let b = c1[k].wrapping_add(predict(0, u1[k] as i32, 0, 32));
432        w[k] = a;
433        w[2 + k] = b;
434        r0[k] = a as u16;
435        r1[k] = b as u16;
436    }
437    let mut col = 2;
438    while col + 1 < width {
439        let a0 = c0[col].wrapping_add(predict(w[0], u0[col] as i32, u0[col - 2] as i32, 32));
440        let a1 = c0[col + 1].wrapping_add(predict(w[1], u0[col + 1] as i32, u0[col - 1] as i32, 32));
441        let b0 = c1[col].wrapping_add(predict(w[2], u1[col] as i32, u1[col - 2] as i32, 32));
442        let b1 = c1[col + 1].wrapping_add(predict(w[3], u1[col + 1] as i32, u1[col - 1] as i32, 32));
443        w = [a0, a1, b0, b1];
444        r0[col] = a0 as u16;
445        r0[col + 1] = a1 as u16;
446        r1[col] = b0 as u16;
447        r1[col + 1] = b1 as u16;
448        col += 2;
449    }
450    if col < width {
451        let a0 = c0[col].wrapping_add(predict(w[0], u0[col] as i32, u0[col - 2] as i32, 32));
452        let b0 = c1[col].wrapping_add(predict(w[2], u1[col] as i32, u1[col - 2] as i32, 32));
453        r0[col] = a0 as u16;
454        r1[col] = b0 as u16;
455    }
456}
457
458/// Edge-sensitive predictor from the west, north and north-west same-colour samples.
459///
460/// Written as data-independent selects: the decision pattern changes from pixel to
461/// pixel, so branches would mispredict constantly.
462#[inline(always)]
463fn predict(w: i32, n: i32, nw: i32, grad: i32) -> i32 {
464    let a = (w < n) | (nw < w);
465    let b = (nw <= n) & (w >= n);
466    let c = (nw < n) | (w >= n);
467    let d = (nw > w) | (w >= n);
468    let smooth = ((n - nw).abs() <= grad) & ((w - nw).abs() <= grad);
469    let g = if smooth { (n + w) >> 1 } else { n + w - nw };
470    let use_w = a & (b | !c);
471    let use_g = a & !b & c & d;
472    let r = if use_g { g } else { n };
473    if use_w { w } else { r }
474}
475
476/// Branchy form of [`predict`], kept as the specification.
477#[cfg(test)]
478fn predict_reference(w: i32, n: i32, nw: i32, grad: i32) -> i32 {
479    let mut n = n;
480    if w < n || nw < w {
481        if nw <= n && w >= n {
482            n = w;
483        } else if nw < n || w >= n {
484            if nw > w || w >= n {
485                if (n - nw).abs() > grad {
486                    n += w - nw;
487                } else if (w - nw).abs() <= grad {
488                    n = (n + w) >> 1;
489                } else {
490                    n = n + w - nw;
491                }
492            }
493        } else {
494            n = w;
495        }
496    }
497    n
498}
499
500/// Any parameter set (slower: parameters are runtime values).
501fn decode_generic(stream: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) -> Result<()> {
502    let mut bits = BitPumpMsb::new(stream);
503    let small_limit = 1u32 << (p.small_shift & 31);
504    let grad = 1i32 << p.grad_shift;
505    let extra = p.extra_bits;
506    let mut ch = [Chan::default(); 2];
507    for row in 0..height {
508        let (above, cur) = out.split_at_mut(row * width);
509        let cur = &mut cur[..width];
510        let up = if row >= 2 { Some(&above[(row - 2) * width..(row - 1) * width]) } else { None };
511        for col in 0..width {
512            let c = &mut ch[col & 1];
513            // Adaptive width of the low part from the previous same-channel code.
514            let (wbits, run) = if col > 1 {
515                let run = if small_limit >= c.prev_code { c.run + 1 } else { 0 };
516                let hb = 32 - (c.prev_code & 0xffff).leading_zeros() as i32;
517                let (t1, t2) = if run >= p.run_switch {
518                    (p.run_threshold, p.run_offset)
519                } else {
520                    (p.base_threshold, p.base_offset)
521                };
522                (t2 + (hb - t1).max(-1) + 1, run)
523            } else {
524                (p.base_offset, 0)
525            };
526            let wbits = wbits.min(p.max_wbits);
527            if !(0..16).contains(&wbits) {
528                return Err(Error::corrupt("ORF: invalid code width"));
529            }
530            let wbits = wbits as u32;
531            c.run = run;
532
533            // Entropy decode one code (fill() guarantees 32 buffered bits).
534            bits.fill();
535            let sign = bits.peek_no_fill(1);
536            bits.skip_no_fill(1);
537            let lowbits = if p.low_bits > 0 {
538                let v = bits.peek_no_fill(p.low_bits);
539                bits.skip_no_fill(p.low_bits);
540                v
541            } else {
542                0
543            };
544            let extrabits = if extra > 0 {
545                let v = bits.peek_no_fill(extra);
546                bits.skip_no_fill(extra);
547                v
548            } else {
549                0
550            };
551            let window = bits.peek_no_fill(p.max_high);
552            let high = if window != 0 {
553                let zeros = window.leading_zeros() - (32 - p.max_high);
554                bits.skip_no_fill(zeros + 1);
555                zeros
556            } else {
557                // Escape: `max_high` zeros, then a fixed-width high part and one spare bit.
558                bits.skip_no_fill(p.max_high);
559                bits.fill();
560                let h = if wbits != 15 {
561                    let n = 15 - wbits;
562                    let v = bits.peek_no_fill(n);
563                    bits.skip_no_fill(n);
564                    v
565                } else {
566                    0
567                };
568                bits.skip_no_fill(1);
569                h
570            };
571            let low = if wbits > 0 {
572                bits.fill();
573                let v = bits.peek_no_fill(wbits);
574                bits.skip_no_fill(wbits);
575                v
576            } else {
577                0
578            };
579            let glc = low | high.wrapping_shl(wbits);
580            let mut code = (glc ^ (sign.wrapping_neg())) as i32;
581            c.prev_code = glc;
582
583            // Combine with the smoothed residual.
584            let v = if col < 2 { 0 } else { c.smooth };
585            let vbits = match p.low_bits {
586                0 => 0,
587                1 => {
588                    code = lowbits as i32 + 2 * (v + code);
589                    v + (code >> 1)
590                }
591                _ => {
592                    code = lowbits as i32 + 4 * (v + code);
593                    ((code >> 1) & !1) + v + (code >> 2)
594                }
595            };
596            c.smooth = if p.smooth_shift >= 15 { 0 } else { vbits >> p.smooth_shift };
597
598            // Edge-sensitive prediction from same-colour neighbours.
599            let w = if col < 2 { p.edge_pred } else { c.west };
600            let (mut n, nw) = match up {
601                Some(u) => ((u[col] >> extra) as i32, if col < 2 { p.edge_pred } else { (u[col - 2] >> extra) as i32 }),
602                None => (p.edge_pred, p.edge_pred),
603            };
604            if w < n || nw < w {
605                if nw <= n && w >= n {
606                    n = w;
607                } else if nw < n || w >= n {
608                    if nw > w || w >= n {
609                        if (n - nw).abs() > grad {
610                            n += w - nw;
611                        } else if (w - nw).abs() <= grad {
612                            n = (n + w) >> 1;
613                        } else {
614                            n = n + w - nw;
615                        }
616                    }
617                } else {
618                    n = w;
619                }
620            }
621            let val = code.wrapping_add(n);
622            c.west = val;
623            cur[col] = (extrabits as i32).wrapping_add(val.wrapping_shl(extra)) as u16;
624        }
625        if bits.exhausted() {
626            return Err(Error::corrupt(format!("ORF: compressed data ends at row {row}/{height}")));
627        }
628    }
629    Ok(())
630}
631
632/// Straightforward reference implementation kept for testing the optimised decoder:
633/// mirrors the camera's coder description one bit at a time.
634#[cfg(test)]
635pub(crate) fn decode_reference(data: &[u8], p: &OlympusParams, out: &mut [u16], width: usize, height: usize) {
636    let start = stream_start(data, p).unwrap();
637    let mut bitpos = start * 8;
638    let mut bit = || {
639        let b = data.get(bitpos / 8).map(|&b| (b >> (7 - bitpos % 8)) & 1).unwrap_or(0) as u32;
640        bitpos += 1;
641        b
642    };
643    let mut ctx = [0i32; 4];
644    let (mut lpred, mut pred1, mut glc, mut gcode_prev) = (0i32, 0i32, 0u32, 0u32);
645    for row in 0..height {
646        for col in 0..width {
647            let pcode = gcode_prev;
648            gcode_prev = glc;
649            let psel = if col > 1 && (1u32 << p.small_shift) >= pcode { ctx[3] + 1 } else { 0 };
650            ctx[3] = ctx[2];
651            ctx[2] = psel;
652            let mut wbits = if col > 1 {
653                let hb = 32 - (pcode & 0xffff).leading_zeros() as i32;
654                let (t1, t2) = if psel >= p.run_switch {
655                    (p.run_threshold, p.run_offset)
656                } else {
657                    (p.base_threshold, p.base_offset)
658                };
659                t2 + (hb - t1).max(-1) + 1
660            } else {
661                p.base_offset
662            };
663            wbits = wbits.min(p.max_wbits);
664            let wbits = wbits as u32;
665            let mut rd = |n: u32| (0..n).fold(0u32, |a, _| a << 1 | bit());
666            let sign = rd(1);
667            let t643 = rd(p.low_bits);
668            let t640 = rd(p.extra_bits);
669            let mut high = 0;
670            loop {
671                if rd(1) == 1 {
672                    break;
673                }
674                high += 1;
675                if high == p.max_high {
676                    high = if wbits != 15 { rd(15 - wbits) } else { 0 };
677                    rd(1);
678                    break;
679                }
680            }
681            let low = rd(wbits);
682            glc = low | (high << wbits);
683            let mut gcode = (glc ^ sign.wrapping_neg()) as i32;
684            let vbits = if p.low_bits == 2 {
685                let v = if col < 2 { 0 } else { pred1 };
686                gcode = t643 as i32 + 4 * (v + gcode);
687                ((gcode >> 1) & !1) + v + (gcode >> 2)
688            } else {
689                0
690            };
691            pred1 = ctx[0];
692            ctx[0] = vbits >> p.smooth_shift;
693            let w = if col < 2 { p.edge_pred } else { ctx[1] };
694            let mut n = if row < 2 { p.edge_pred } else { (out[(row - 2) * width + col] >> p.extra_bits) as i32 };
695            let nw = if row < 2 || col < 2 {
696                p.edge_pred
697            } else {
698                (out[(row - 2) * width + col - 2] >> p.extra_bits) as i32
699            };
700            ctx[1] = lpred;
701            if w < n || nw < w {
702                if nw <= n && w >= n {
703                    n = w;
704                } else if nw < n || w >= n {
705                    if nw > w || w >= n {
706                        if (n - nw).abs() > 1 << p.grad_shift {
707                            n += w - nw;
708                        } else if (w - nw).abs() <= 1 << p.grad_shift {
709                            n = (n + w) >> 1;
710                        } else {
711                            n = n + w - nw;
712                        }
713                    }
714                } else {
715                    n = w;
716                }
717            }
718            lpred = gcode + n;
719            out[row * width + col] = (t640 as i32 + ((gcode + n) << p.extra_bits)) as u16;
720        }
721    }
722}
723
724#[cfg(test)]
725mod tests {
726    use super::*;
727
728    fn stream(seed: u32, len: usize, header_len: usize) -> Vec<u8> {
729        let mut v = vec![0u8; header_len - 1];
730        v.push(1);
731        v.extend([0, 0]);
732        let mut x = seed;
733        // Biased towards small codes so the stream exercises runs and escapes.
734        v.extend((0..len).map(|i| {
735            x = x.wrapping_mul(1664525).wrapping_add(1013904223);
736            let b = (x >> 24) as u8;
737            if i % 7 == 0 { b & 0x0f } else { b | 0x11 }
738        }));
739        v
740    }
741
742    #[test]
743    fn optimised_matches_reference_on_random_streams() {
744        for seed in 1..12 {
745            let p = OlympusParams::default();
746            let data = stream(seed, 40_000, 5);
747            let (w, h) = (66, 40);
748            let mut a = vec![0u16; w * h];
749            let mut b = vec![0u16; w * h];
750            let _ = decode(&data, &p, &mut a, w, h);
751            decode_reference(&data, &p, &mut b, w, h);
752            assert_eq!(a, b, "seed {seed}");
753            // Pipelined (two-thread) decoding gives the same result.
754            let mut c = vec![0u16; w * h];
755            let _ = decode_with(&data, &p, &mut c, w, h, true);
756            assert_eq!(c, b, "parallel seed {seed}");
757        }
758    }
759
760    #[test]
761    fn predictor_matches_specification() {
762        let vals = [-70, -33, -32, -31, -1, 0, 1, 2, 31, 32, 33, 64, 100, 4095];
763        for &w in &vals {
764            for &n in &vals {
765                for &nw in &vals {
766                    assert_eq!(predict(w, n, nw, 32), predict_reference(w, n, nw, 32), "{w} {n} {nw}");
767                }
768            }
769        }
770    }
771
772    #[test]
773    fn generic_path_matches_reference() {
774        let p = OlympusParams { grad_shift: 4, ..OlympusParams::default() };
775        for seed in 1..6 {
776            let data = stream(seed, 40_000, 5);
777            let (w, h) = (34, 30);
778            let mut a = vec![0u16; w * h];
779            let mut b = vec![0u16; w * h];
780            let _ = decode(&data, &p, &mut a, w, h);
781            decode_reference(&data, &p, &mut b, w, h);
782            assert_eq!(a, b, "seed {seed}");
783        }
784    }
785
786    #[test]
787    fn header_and_truncation_checks() {
788        let p = OlympusParams::default();
789        let mut out = vec![0u16; 64 * 64];
790        assert!(decode(&[0, 0, 0, 0, 2, 0, 0, 0xff], &p, &mut out, 64, 64).is_err());
791        let data = stream(3, 200, 5);
792        assert!(decode(&data, &p, &mut out, 64, 64).is_err());
793        let bad = OlympusParams { max_wbits: 40, ..p };
794        assert!(decode(&stream(3, 100_000, 5), &bad, &mut out, 64, 64).is_err());
795    }
796}