Skip to main content

flashraw_codecs/
nikon_packed.rs

1//! The "load flags" family of bit-packed layouts.
2//!
3//! Many Nikon, Pentax and Samsung uncompressed raws are described by a handful of
4//! "load flags" rather than by standard TIFF packing:
5//!
6//! | flag bits | meaning                                                                |
7//! |-----------|------------------------------------------------------------------------|
8//! | `1`       | one padding byte after every 10 samples (Nikon D100 "compressed")       |
9//! | `2`       | rows stored as two interlaced fields (even rows, then odd rows)         |
10//! | `4`       | the second field starts at a separate offset                           |
11//! | `8 \| 16` | bits are gathered in little-endian units of 16/24/32 bits, MSB first   |
12//! | `64`      | swap each pair of output columns                                       |
13//! | `128`     | rows are padded to an even number of bytes                             |
14//!
15//! [`PackedSpec::fast_layout`] maps the common combinations onto
16//! [`crate::packed`] (parallel, SIMD); everything else uses the exact sequential
17//! path in [`decode`], which is the specification.
18
19use crate::packed::{self, PackedLayout};
20use flashraw_core::{Error, PackOrder, Result};
21
22/// A load-flags packed raster description.
23#[derive(Debug, Clone, Copy)]
24pub struct PackedSpec {
25    pub width: usize,
26    pub height: usize,
27    /// Bits per sample (1..=16).
28    pub bits: u32,
29    /// Load flags (see the module docs).
30    pub flags: u32,
31    /// Byte offset (relative to the start of `src`) of the second field when `flags & 4`.
32    pub field2_offset: Option<usize>,
33}
34
35impl PackedSpec {
36    /// Bytes per row before the 10-sample padding.
37    fn bwide(&self) -> usize {
38        let b = self.width * self.bits as usize / 8;
39        b + (b & ((self.flags as usize >> 7) & 1))
40    }
41
42    /// Padding bits dropped at the end of each row (negative: rows share bytes).
43    fn rbits(&self) -> i64 {
44        self.bwide() as i64 * 8 - (self.width * self.bits as usize) as i64
45    }
46
47    /// Bytes per row in the file.
48    pub fn row_bytes(&self) -> usize {
49        let b = self.bwide();
50        if self.flags & 1 != 0 { b * 16 / 15 } else { b }
51    }
52
53    /// Size of the gathering unit in bits.
54    fn bite(&self) -> u32 {
55        8 + (self.flags & 24)
56    }
57
58    /// The equivalent standard packed layout, when one exists.
59    pub fn fast_layout(&self) -> Option<PackedLayout> {
60        if self.flags & 7 != 0 || self.rbits() < 0 {
61            return None;
62        }
63        let rbits = self.rbits() as usize;
64        let swap = self.flags & 64 != 0;
65        let order = match (self.bite(), swap) {
66            (8, false) => PackOrder::MsbBytes,
67            // 24-bit little-endian units with swapped column pairs == LSB-first 12-bit packing.
68            (24, true) if self.bits == 12 => PackOrder::LsbBytes,
69            (16, false) if self.bits == 16 || (self.width * self.bits as usize) % 16 == 0 => PackOrder::Msb16,
70            (32, false) if (self.width * self.bits as usize) % 32 == 0 => PackOrder::Msb32,
71            _ => return None,
72        };
73        if rbits % 8 != 0 || (order != PackOrder::MsbBytes && order != PackOrder::LsbBytes && rbits != 0) {
74            return None;
75        }
76        Some(PackedLayout {
77            width: self.width,
78            height: self.height,
79            bits: self.bits,
80            order,
81            row_stride: self.row_bytes(),
82        })
83    }
84
85    /// Minimum input size for a complete image.
86    pub fn required_bytes(&self) -> usize {
87        if self.flags & 4 != 0 {
88            let half = self.height.div_ceil(2);
89            self.field2_offset.unwrap_or(0) + (self.height - half) * self.row_bytes()
90        } else {
91            self.height * self.row_bytes()
92        }
93    }
94}
95
96/// Decode `src` into `out` (rows of `stride` samples). Truncated input is an error
97/// (decoding aborts at the first row that starts past EOF) unless `lenient`
98/// (recovering truncated data): then every complete sample is decoded and the rest are
99/// left zero (`out` must be zeroed).
100pub fn decode(
101    src: &[u8],
102    spec: &PackedSpec,
103    out: &mut [u16],
104    stride: usize,
105    parallel: bool,
106    simd: bool,
107    lenient: bool,
108) -> Result<()> {
109    if spec.bits == 0 || spec.bits > 16 || spec.width == 0 {
110        return Err(Error::corrupt(format!("unsupported packed layout ({} bits)", spec.bits)));
111    }
112    if stride < spec.width || out.len() < stride * spec.height {
113        return Err(Error::InvalidArgument("packed: output too small".into()));
114    }
115    if let Some(layout) = spec.fast_layout() {
116        return packed::unpack(src, layout, out, stride, parallel, simd, lenient);
117    }
118    decode_exact_with(src, spec, out, stride, lenient)
119}
120
121/// Exact sequential decode of any load-flags layout (the specification).
122pub fn decode_exact(src: &[u8], spec: &PackedSpec, out: &mut [u16], stride: usize) -> Result<()> {
123    decode_exact_with(src, spec, out, stride, false)
124}
125
126/// [`decode_exact`]; `lenient` stops at the first sample that needs bits past the end of
127/// `src` and zeroes it and the rest of its row instead of failing at the next row.
128fn decode_exact_with(src: &[u8], spec: &PackedSpec, out: &mut [u16], stride: usize, lenient: bool) -> Result<()> {
129    let (w, h, bits) = (spec.width, spec.height, spec.bits as i32);
130    let bite = spec.bite() as i32;
131    let rbits = spec.rbits();
132    let half = h.div_ceil(2);
133    let swap = ((spec.flags >> 6) & 1) as usize;
134    let mut pos = 0usize;
135    let mut bitbuf: u64 = 0;
136    let mut vbits: i64 = 0;
137    let mut eof = false;
138    for irow in 0..h {
139        let mut row = irow;
140        if spec.flags & 2 != 0 {
141            row = irow % half * 2 + irow / half;
142            if row == 1 && spec.flags & 4 != 0 {
143                vbits = 0;
144                pos = spec.field2_offset.ok_or_else(|| Error::corrupt("missing second field offset"))?;
145            }
146        }
147        if eof {
148            return Err(Error::corrupt(format!("truncated packed data at row {irow}/{h}")));
149        }
150        let line =
151            out.get_mut(row * stride..row * stride + w).ok_or_else(|| Error::corrupt("packed row out of range"))?;
152        for col in 0..w {
153            vbits -= bits as i64;
154            while vbits < 0 {
155                bitbuf = if bite >= 64 { 0 } else { bitbuf << bite };
156                for i in (0..bite).step_by(8) {
157                    let b = match src.get(pos) {
158                        Some(&b) => b as u64,
159                        None => {
160                            eof = true;
161                            0
162                        }
163                    };
164                    pos += 1;
165                    bitbuf |= b << i;
166                }
167                vbits += bite as i64;
168            }
169            if eof && lenient {
170                for c in col..w {
171                    if let Some(v) = line.get_mut(c ^ swap) {
172                        *v = 0;
173                    }
174                }
175                return Ok(());
176            }
177            let val = ((bitbuf << (64 - bits as i64 - vbits)) >> (64 - bits)) as u16;
178            line[col ^ swap] = val;
179            if spec.flags & 1 != 0 && col % 10 == 9 {
180                pos += 1;
181            }
182        }
183        vbits -= rbits;
184    }
185    Ok(())
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    fn pseudo(n: usize, seed: u64) -> Vec<u8> {
193        let mut x = seed | 1;
194        (0..n)
195            .map(|_| {
196                x ^= x << 13;
197                x ^= x >> 7;
198                x ^= x << 17;
199                x as u8
200            })
201            .collect()
202    }
203
204    #[test]
205    fn fast_layouts_match_exact_emulation() {
206        for (bits, flags, width, fast) in [
207            (12u32, 0u32, 40usize, true),
208            (12, 80, 40, true),
209            (12, 24, 40, true),
210            (14, 0, 64, true),
211            (12, 128, 42, true),
212            (12, 128, 41, false),
213            (16, 0, 10, true),
214            (12, 16, 40, false),
215            (12, 0, 41, false),
216        ] {
217            let spec = PackedSpec { width, height: 7, bits, flags, field2_offset: None };
218            let src = pseudo(spec.required_bytes() + 8, bits as u64 * 31 + flags as u64);
219            let mut a = vec![0u16; width * 7];
220            let mut b = vec![0u16; width * 7];
221            decode_exact(&src, &spec, &mut a, width).unwrap();
222            assert_eq!(spec.fast_layout().is_some(), fast, "{bits} {flags} {width}");
223            decode(&src, &spec, &mut b, width, false, true, false).unwrap();
224            assert_eq!(a, b, "bits {bits} flags {flags}");
225        }
226    }
227
228    #[test]
229    fn lsb24_swap_is_lsb_packing() {
230        // Two samples 0xABC, 0x123 in LSB-first 12-bit packing: BC 3A 12.
231        let src = [0xBC, 0x3A, 0x12];
232        let spec = PackedSpec { width: 2, height: 1, bits: 12, flags: 80, field2_offset: None };
233        let mut out = [0u16; 2];
234        decode_exact(&src, &spec, &mut out, 2).unwrap();
235        assert_eq!(out, [0xABC, 0x123]);
236    }
237
238    #[test]
239    fn padding_every_ten_samples() {
240        // 10 samples of 12 bits = 15 bytes + 1 pad byte.
241        let mut src = vec![0u8; 32];
242        src[0] = 0xFF;
243        src[1] = 0xF0; // sample 0 = 0xFFF
244        src[15] = 0xAA; // padding byte, ignored
245        src[16] = 0x80; // sample 10 = 0x800
246        let spec = PackedSpec { width: 20, height: 1, bits: 12, flags: 1, field2_offset: None };
247        let mut out = vec![0u16; 20];
248        decode(&src, &spec, &mut out, 20, false, false, false).unwrap();
249        assert_eq!(out[0], 0xFFF);
250        assert_eq!(out[10], 0x800);
251        assert_eq!(spec.row_bytes(), 32);
252    }
253
254    #[test]
255    fn interlaced_fields() {
256        let spec = PackedSpec { width: 2, height: 4, bits: 8, flags: 2, field2_offset: None };
257        let src = [0u8, 1, 2, 3, 4, 5, 6, 7];
258        let mut out = vec![0u16; 8];
259        decode(&src, &spec, &mut out, 2, false, false, false).unwrap();
260        // Stored rows: 0, 2, 1, 3.
261        assert_eq!(out, vec![0, 1, 4, 5, 2, 3, 6, 7]);
262    }
263
264    #[test]
265    fn truncation_is_an_error() {
266        let spec = PackedSpec { width: 16, height: 8, bits: 12, flags: 1, field2_offset: None };
267        let src = vec![0u8; 40];
268        let mut out = vec![0u16; 16 * 8];
269        assert!(decode(&src, &spec, &mut out, 16, false, false, false).is_err());
270        let spec = PackedSpec { width: 16, height: 8, bits: 12, flags: 0, field2_offset: None };
271        assert!(decode(&src, &spec, &mut out, 16, false, false, false).is_err());
272    }
273
274    #[test]
275    fn lenient_keeps_complete_samples() {
276        // Exact path (flags 1): 40 bytes hold the first two 32-byte rows' worth of data
277        // only partly; complete samples match a full decode, the rest are zero.
278        let spec = PackedSpec { width: 16, height: 4, bits: 12, flags: 1, field2_offset: None };
279        let full = pseudo(spec.required_bytes(), 7);
280        let mut a = vec![0u16; 16 * 4];
281        decode(&full, &spec, &mut a, 16, false, false, false).unwrap();
282        let mut b = vec![0u16; 16 * 4];
283        decode(&full[..40], &spec, &mut b, 16, false, false, true).unwrap();
284        assert_eq!(&b[..16], &a[..16]);
285        let kept = b[16..32].iter().take_while(|&&v| v != 0).count();
286        assert!(kept > 0 && kept < 16);
287        assert_eq!(&b[16..16 + kept], &a[16..16 + kept]);
288        assert!(b[16 + kept..].iter().all(|&v| v == 0));
289    }
290}