Skip to main content

flashraw_codecs/
phaseone.rs

1//! Phase One IIQ entropy coders.
2//!
3//! IIQ stores a row-offset table, so every row is an independent bit stream (the
4//! container decodes rows in parallel). Two coders exist:
5//!
6//! - **IIQ L** ("raw_c", formats 3/5/8): per 8-pixel block and per column parity a
7//!   prefix code selects a difference width from a 10-entry table; width 14 is an
8//!   escape for a literal 16-bit value. Samples are 14-bit (shifted left by two into
9//!   the 16-bit range) except for format 8, which carries native 16-bit values.
10//!   Format 5 additionally maps values below 256 through a square-law curve.
11//! - **IIQ S** (format 6): per 8-pixel block, each parity keeps a "bit check" level
12//!   (adjusted by a delta code or reset through an index table) that sets the
13//!   quantisation step of the coded differences; level 9 is a literal escape.
14//!
15//! Both read bits MSB-first from 32-bit little-endian words ([`BitPumpMsb32`]) for
16//! little-endian files (the big-endian variant uses [`BitPumpMsb`]).
17//!
18//! [`BitPumpMsb32`]: flashraw_core::bits::BitPumpMsb32
19//! [`BitPumpMsb`]: flashraw_core::bits::BitPumpMsb
20
21use flashraw_core::bits::BitRead;
22use flashraw_core::{Error, Result};
23
24/// Difference widths selected by the IIQ L prefix code.
25const L_LENGTHS: [u32; 10] = [8, 7, 6, 9, 11, 10, 5, 12, 14, 13];
26
27/// Variant parameters of the IIQ L coder.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct IiqLParams {
30    /// Shift applied to decoded values (2 for 14-bit data, 0 for format 8).
31    pub shift: u32,
32    /// Format 5: values below 256 go through the square-law curve.
33    pub low_curve: bool,
34}
35
36/// Format-5 low-range curve: `round(i^2 / 3.969)` (evaluated in single precision, which
37/// the stored values depend on bit for bit).
38fn low_curve_table() -> [u16; 256] {
39    let mut t = [0u16; 256];
40    for (i, v) in t.iter_mut().enumerate() {
41        *v = ((i * i) as f32 / 3.969f32 + 0.5f32) as u16;
42    }
43    t
44}
45
46/// Decode one IIQ L row of `out.len()` samples from a bit pump positioned at the row start.
47///
48/// Exact semantics: the predictor is a 32-bit accumulator
49/// (out-of-range sums wrap when stored), and blocks whose prefix is a single `1` bit
50/// keep the previous width.
51// Inlined into the per-row caller: as an out-of-line function the bit-pump calls lose
52// their register allocation on aarch64 (measured 35% slower on Apple M1).
53#[inline(always)]
54pub fn decode_l_row<B: BitRead>(bits: &mut B, out: &mut [u16], p: IiqLParams) -> Result<()> {
55    let width = out.len();
56    let full_blocks = width & !7;
57    let curve = if p.low_curve { Some(low_curve_table()) } else { None };
58    let mut len = [0u32; 2];
59    let mut pred = [0i32; 2];
60    for (col, o) in out.iter_mut().enumerate() {
61        if col >= full_blocks {
62            len = [14, 14];
63        } else if col & 7 == 0 {
64            for l in len.iter_mut() {
65                // Up to five zero bits terminated by a one; a non-zero count selects a
66                // length together with one more bit. One peek replaces the bit loop.
67                bits.fill();
68                let peek = bits.peek_no_fill(7);
69                let top = peek >> 2;
70                let zeros = if top == 0 { 5 } else { top.leading_zeros() as usize - 27 };
71                let mut used = if zeros < 5 { zeros as u32 + 1 } else { 5 };
72                if zeros > 0 {
73                    let extra = (peek >> (6 - used)) & 1;
74                    used += 1;
75                    *l = L_LENGTHS[(zeros - 1) * 2 + extra as usize];
76                }
77                bits.skip_no_fill(used);
78            }
79        }
80        let parity = col & 1;
81        let w = len[parity];
82        let v = match w {
83            14 => {
84                let v = bits.get(16) as i32;
85                pred[parity] = v;
86                v
87            }
88            0 => return Err(Error::corrupt("IIQ L: block without difference width")),
89            _ => {
90                let d = bits.get(w) as i32 + 1 - (1 << (w - 1));
91                pred[parity] = pred[parity].wrapping_add(d);
92                pred[parity]
93            }
94        };
95        let mut s = v as u16;
96        if let Some(c) = &curve {
97            if s < 256 {
98                s = c[s as usize];
99            }
100        }
101        *o = s << p.shift;
102    }
103    if bits.exhausted() {
104        return Err(Error::corrupt("IIQ L: truncated row data"));
105    }
106    Ok(())
107}
108
109const S_USED_CORR: [u32; 8] = [3, 3, 3, 3, 1, 1, 1, 1];
110const S_EXTRA_BITS: [u32; 8] = [1, 2, 3, 4, 0, 0, 0, 0];
111const S_BIT_INDICATOR: [u32; 32] =
112    [9, 8, 0, 7, 6, 6, 5, 5, 1, 1, 1, 1, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2];
113const S_SKIP_BITS: [u32; 32] =
114    [5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];
115
116/// Shift with x86 semantics (count taken modulo 32), applied to out-of-range levels in
117/// malformed blocks; masking the count keeps output well defined without undefined
118/// behaviour.
119#[inline(always)]
120fn shl32(v: u32, s: u32) -> u32 {
121    v.wrapping_shl(s)
122}
123
124/// Read the level update of one parity in an IIQ S block.
125#[inline(always)]
126fn s_level<B: BitRead>(bits: &mut B, level: &mut u32) {
127    let idx = bits.peek(7);
128    bits.skip(2);
129    if idx >= 32 {
130        *level = (idx >> 5).wrapping_add(*level).wrapping_sub(2);
131    } else {
132        *level = S_BIT_INDICATOR[idx as usize];
133        bits.skip(S_SKIP_BITS[idx as usize]);
134    }
135}
136
137/// Decode one IIQ S row (output already scaled to 16 bits).
138pub fn decode_s_row<B: BitRead>(bits: &mut B, out: &mut [u16]) -> Result<()> {
139    let width = out.len();
140    let init_bits = bits.get(16) & 7;
141    let blocks = width / 8;
142    let mut level = [0u32; 2];
143    let mut prev = [0i32; 2];
144    let pix_sub_init = 17u32.wrapping_sub(init_bits);
145    for blk in out[..blocks * 8].chunks_exact_mut(8) {
146        s_level(bits, &mut level[0]);
147        s_level(bits, &mut level[1]);
148        let bidx = bits.peek(3) as usize;
149        bits.skip(S_USED_CORR[bidx]);
150        let extra = S_EXTRA_BITS[bidx];
151        let take = init_bits + extra;
152        let shift = [level[0].wrapping_sub(extra), level[1].wrapping_sub(extra)];
153        let sub = [
154            0xFFFFu32.wrapping_shr(pix_sub_init.wrapping_sub(level[0])) as i32,
155            0xFFFFu32.wrapping_shr(pix_sub_init.wrapping_sub(level[1])) as i32,
156        ];
157        for (i, o) in blk.iter_mut().enumerate() {
158            let p = i & 1;
159            let value = if level[p] == 9 {
160                bits.get(14) as i32
161            } else {
162                let coded = if take == 0 { 0 } else { bits.get(take) };
163                prev[p].wrapping_add(shl32(coded, shift[p]) as i32).wrapping_sub(sub[p])
164            };
165            *o = value.wrapping_shl(2).clamp(0, 0xFFFF) as u16;
166            prev[p] = value;
167        }
168    }
169    for o in out[blocks * 8..].iter_mut() {
170        *o = (bits.get(14) << 2) as u16;
171    }
172    if bits.exhausted() {
173        return Err(Error::corrupt("IIQ S: truncated row data"));
174    }
175    Ok(())
176}
177
178/// Undo the key scrambling of uncompressed IIQ formats 1 and 2 on a run of samples
179/// (processed in pairs over the whole raster, independent of row boundaries).
180pub fn unscramble_pairs(samples: &mut [u16], akey: u16, bkey: u16, format: u32) {
181    let mask: u16 = if format == 1 { 0x5555 } else { 0x1354 };
182    for pair in samples.chunks_exact_mut(2) {
183        let a = pair[0] ^ akey;
184        let b = pair[1] ^ bkey;
185        pair[0] = (a & mask) | (b & !mask);
186        pair[1] = (b & mask) | (a & !mask);
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use flashraw_core::bits::BitPumpMsb32;
194
195    /// MSB-first bit writer emitting 32-bit little-endian words.
196    #[derive(Default)]
197    pub struct Writer {
198        words: Vec<u32>,
199        acc: u64,
200        n: u32,
201    }
202    impl Writer {
203        pub fn put(&mut self, v: u32, bits: u32) {
204            if bits == 0 {
205                return;
206            }
207            self.acc = (self.acc << bits) | (v as u64 & ((1u64 << bits) - 1));
208            self.n += bits;
209            while self.n >= 32 {
210                self.words.push((self.acc >> (self.n - 32)) as u32);
211                self.n -= 32;
212            }
213        }
214        pub fn finish(mut self) -> Vec<u8> {
215            if self.n > 0 {
216                self.words.push((self.acc << (32 - self.n)) as u32);
217            }
218            self.words.push(0);
219            self.words.iter().flat_map(|w| w.to_le_bytes()).collect()
220        }
221    }
222
223    /// Reference IIQ L encoder (14-bit samples, shift 2).
224    fn encode_l(samples: &[u16]) -> Vec<u8> {
225        let mut w = Writer::default();
226        let width = samples.len();
227        let mut pred = [0i32; 2];
228        let mut len = [0u32; 2];
229        for col in 0..width {
230            if col >= width & !7 {
231                len = [14, 14];
232            } else if col & 7 == 0 {
233                for p in 0..2 {
234                    let mut need = 0;
235                    let mut pr = pred[p];
236                    for c in (col + p..col + 8).step_by(2) {
237                        let d = samples[c] as i32 - pr;
238                        pr = samples[c] as i32;
239                        need = need.max(d.abs() + 1);
240                    }
241                    let mut best = 8; // index of width 14
242                    for (k, &l) in L_LENGTHS.iter().enumerate() {
243                        if l != 14 && (1 << (l - 1)) > need && L_LENGTHS[k] < L_LENGTHS[best] {
244                            best = k;
245                        }
246                    }
247                    let j = best / 2;
248                    for _ in 0..=j {
249                        w.put(0, 1);
250                    }
251                    if j + 1 < 5 {
252                        w.put(1, 1);
253                    }
254                    w.put((best & 1) as u32, 1);
255                    len[p] = L_LENGTHS[best];
256                }
257            }
258            let p = col & 1;
259            let v = samples[col] as i32;
260            if len[p] == 14 {
261                w.put(v as u32, 16);
262            } else {
263                let l = len[p];
264                w.put((v - pred[p] - 1 + (1 << (l - 1))) as u32, l);
265            }
266            pred[p] = v;
267        }
268        w.finish()
269    }
270
271    #[test]
272    fn l_round_trip() {
273        let mut seed = 12345u32;
274        for width in [1usize, 7, 8, 9, 16, 63, 200] {
275            let samples: Vec<u16> = (0..width)
276                .map(|i| {
277                    seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
278                    let base = 2000 + (i as i32 * 37 % 900);
279                    (base + (seed >> 20) as i32 % 300) as u16 & 0x3FFF
280                })
281                .collect();
282            let data = encode_l(&samples);
283            let mut out = vec![0u16; width];
284            decode_l_row(&mut BitPumpMsb32::new(&data), &mut out, IiqLParams { shift: 2, low_curve: false }).unwrap();
285            let expect: Vec<u16> = samples.iter().map(|v| v << 2).collect();
286            assert_eq!(out, expect, "width {width}");
287        }
288    }
289
290    #[test]
291    fn l_format8_and_curve() {
292        let samples: Vec<u16> = vec![10, 300, 255, 40000, 3, 2, 1, 0, 100];
293        let data = encode_l(&samples);
294        let mut out = vec![0u16; samples.len()];
295        decode_l_row(&mut BitPumpMsb32::new(&data), &mut out, IiqLParams { shift: 0, low_curve: false }).unwrap();
296        assert_eq!(out, samples);
297        decode_l_row(&mut BitPumpMsb32::new(&data), &mut out, IiqLParams { shift: 2, low_curve: true }).unwrap();
298        let c = low_curve_table();
299        assert_eq!(out[0], c[10] << 2);
300        assert_eq!(out[1], 300 << 2);
301        assert_eq!(c[255], 16383);
302    }
303
304    #[test]
305    fn l_truncated_is_error() {
306        let samples: Vec<u16> = (0..64).map(|i| (i * 211 % 16000) as u16).collect();
307        let data = encode_l(&samples);
308        let mut out = vec![0u16; 64];
309        let r = decode_l_row(&mut BitPumpMsb32::new(&data[..8]), &mut out, IiqLParams { shift: 2, low_curve: false });
310        assert!(r.is_err());
311    }
312
313    /// IIQ S: random level updates and correction modes; expected output follows the
314    /// documented value formula independently of the bit parsing.
315    #[test]
316    fn s_random_stream() {
317        let mut seed = 99u32;
318        let mut rnd = |n: u32| {
319            seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
320            (seed >> 8) % n
321        };
322        for width in [8usize, 16, 27, 64] {
323            let init_bits = 3u32;
324            let mut w = Writer::default();
325            w.put(init_bits, 16);
326            let mut expect = vec![0u16; width];
327            let mut level = [0u32; 2];
328            let mut prev = [0i32; 2];
329            for blk in 0..width / 8 {
330                for lv in level.iter_mut() {
331                    // Either reset through the index table or apply a delta (when valid).
332                    if blk == 0 || !(6..=7).contains(&*lv) || rnd(2) == 0 {
333                        let target = [9u32, 8, 7, 6, 5][rnd(5) as usize];
334                        let idx = S_BIT_INDICATOR.iter().position(|&b| b == target).unwrap() as u32;
335                        let skip = S_SKIP_BITS[idx as usize];
336                        w.put(idx >> (5 - skip), 2 + skip);
337                        *lv = target;
338                    } else {
339                        let top = 1 + rnd(3); // 1..=3 -> delta -1..=+1
340                        w.put(top, 2);
341                        *lv = *lv + top - 2;
342                    }
343                }
344                let bidx = rnd(8);
345                if bidx < 4 {
346                    w.put(bidx, 3);
347                } else {
348                    w.put(1, 1);
349                }
350                let extra = S_EXTRA_BITS[bidx as usize];
351                let take = init_bits + extra;
352                for i in 0..8 {
353                    let p = i & 1;
354                    let value = if level[p] == 9 {
355                        let v = rnd(1 << 14);
356                        w.put(v, 14);
357                        v as i32
358                    } else {
359                        let coded = rnd(1 << take);
360                        w.put(coded, take);
361                        let sub = (0xFFFFu32 >> (17 - init_bits - level[p])) as i32;
362                        prev[p] + ((coded << (level[p] - extra)) as i32) - sub
363                    };
364                    expect[blk * 8 + i] = (value << 2).clamp(0, 0xFFFF) as u16;
365                    prev[p] = value;
366                }
367            }
368            for e in expect.iter_mut().skip(width / 8 * 8) {
369                let v = rnd(1 << 14);
370                w.put(v, 14);
371                *e = (v << 2) as u16;
372            }
373            let data = w.finish();
374            let mut out = vec![0u16; width];
375            decode_s_row(&mut BitPumpMsb32::new(&data), &mut out).unwrap();
376            assert_eq!(out, expect, "width {width}");
377        }
378    }
379
380    #[test]
381    fn s_garbage_does_not_panic() {
382        let mut seed = 7u32;
383        for len in [0usize, 3, 17, 300] {
384            let data: Vec<u8> = (0..len)
385                .map(|_| {
386                    seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
387                    (seed >> 24) as u8
388                })
389                .collect();
390            let mut out = vec![0u16; 100];
391            let _ = decode_s_row(&mut BitPumpMsb32::new(&data), &mut out);
392            let _ = decode_l_row(&mut BitPumpMsb32::new(&data), &mut out, IiqLParams { shift: 2, low_curve: true });
393        }
394    }
395
396    #[test]
397    fn unscramble_is_involution_for_mask_swap() {
398        let orig = [0x1234u16, 0xABCD, 0xFFFF, 0x0000];
399        let mut s = orig;
400        unscramble_pairs(&mut s, 0, 0, 1);
401        // With zero keys, applying twice swaps the masked bits back.
402        unscramble_pairs(&mut s, 0, 0, 1);
403        assert_eq!(s, orig);
404    }
405}