Skip to main content

flashraw_codecs/
deflate.rs

1//! Deflate (zlib) payloads and TIFF/DNG predictors.
2//!
3//! DNG compression 8 stores tiles as zlib streams, optionally with predictor
4//! 2 (horizontal integer differencing), 34892-34894 (2/4-sample horizontal
5//! differencing) or 3/34894-34895 (floating-point byte-plane differencing).
6
7use flashraw_core::{Error, Result};
8
9/// Inflate a zlib stream, refusing to produce more than `max_out` bytes.
10pub fn inflate_zlib(data: &[u8], max_out: usize) -> Result<Vec<u8>> {
11    // Deflate cannot expand more than 1032:1, so the preallocation hint never needs to
12    // exceed that multiple of the input, whatever the caller allows as a maximum.
13    let hint = max_out.min(data.len().saturating_mul(1032).saturating_add(64));
14    let opts = zune_inflate::DeflateOptions::default().set_limit(max_out).set_size_hint(hint);
15    let mut d = zune_inflate::DeflateDecoder::new_with_options(data, opts);
16    d.decode_zlib().map_err(|e| Error::corrupt(format!("deflate: {e:?}")))
17}
18
19/// Inflate a zlib stream into `out`, returning the number of bytes produced. Decodes in
20/// place into caller memory (no allocation); errors on corrupt data. Output beyond the
21/// stream's length is left untouched; a stream larger than `out` is an error.
22pub fn inflate_zlib_into(data: &[u8], out: &mut [u8]) -> Result<usize> {
23    let (res, rc) = zlib_rs::decompress_slice(out, data, zlib_rs::InflateConfig::default());
24    match rc {
25        zlib_rs::ReturnCode::Ok => Ok(res.len()),
26        rc => Err(Error::corrupt(format!("deflate: {rc:?}"))),
27    }
28}
29
30/// Undo horizontal integer differencing on u16 samples, in place, for one row
31/// of `width` pixels with `cpp` interleaved samples and difference `factor`
32/// (1 for TIFF predictor 2; 2 or 4 for DNG predictors 34892/34893).
33pub fn undo_horizontal_u16(row: &mut [u16], cpp: usize, factor: usize) {
34    let step = cpp * factor;
35    for i in step..row.len() {
36        row[i] = row[i].wrapping_add(row[i - step]);
37    }
38}
39
40/// Undo DNG floating-point predictor (TIFF predictor 3) for one row: bytes were
41/// split into byte planes (most significant first) and then byte-differenced.
42/// `row` holds `width * cpp * bytes_per_sample` bytes; output is native samples in
43/// big-endian byte order ready for [`float_bytes_to_f32`].
44pub fn undo_float_predictor(row: &mut [u8], tmp: &mut Vec<u8>, width_samples: usize, bps_bytes: usize, factor: usize) {
45    let n = width_samples * bps_bytes;
46    if row.len() < n {
47        return;
48    }
49    let row = &mut row[..n];
50    for i in factor..n {
51        row[i] = row[i].wrapping_add(row[i - factor]);
52    }
53    tmp.clear();
54    tmp.resize(n, 0);
55    for s in 0..width_samples {
56        for b in 0..bps_bytes {
57            tmp[s * bps_bytes + b] = row[b * width_samples + s];
58        }
59    }
60    row.copy_from_slice(tmp);
61}
62
63/// Decode one row of DNG floating-point samples straight into `out`.
64///
65/// `row` holds the row's bytes (modified in place). With `factor > 0` the row uses the
66/// floating-point predictor: bytes are byte-differenced with distance `factor`, then
67/// stored as byte planes (most significant plane first). With `factor == 0` the samples
68/// are stored contiguously in file byte order (`little_endian`). Fuses what
69/// [`undo_float_predictor`] + [`float_bytes_to_f32`] do, without a temporary row.
70pub fn decode_float_row(
71    row: &mut [u8],
72    samples: usize,
73    bps_bytes: usize,
74    factor: usize,
75    little_endian: bool,
76    out: &mut [f32],
77) {
78    let n = samples * bps_bytes;
79    if row.len() < n || out.len() < samples || !(2..=4).contains(&bps_bytes) {
80        return;
81    }
82    let row = &mut row[..n];
83    let out = &mut out[..samples];
84    if factor == 0 {
85        match (bps_bytes, little_endian) {
86            (4, true) => out
87                .iter_mut()
88                .zip(row.chunks_exact(4))
89                .for_each(|(o, c)| *o = f32::from_le_bytes([c[0], c[1], c[2], c[3]])),
90            (4, false) => out
91                .iter_mut()
92                .zip(row.chunks_exact(4))
93                .for_each(|(o, c)| *o = f32::from_be_bytes([c[0], c[1], c[2], c[3]])),
94            (2, le) => out.iter_mut().zip(row.chunks_exact(2)).for_each(|(o, c)| {
95                *o = f16_to_f32(if le { u16::from_le_bytes([c[0], c[1]]) } else { u16::from_be_bytes([c[0], c[1]]) })
96            }),
97            (_, le) => out.iter_mut().zip(row.chunks_exact(3)).for_each(|(o, c)| {
98                let (a, b, d) = if le { (c[2], c[1], c[0]) } else { (c[0], c[1], c[2]) };
99                *o = f24_to_f32((a as u32) << 16 | (b as u32) << 8 | d as u32)
100            }),
101        }
102        return;
103    }
104    // Undo byte differencing (sequential prefix sum with distance `factor`).
105    if factor == 1 {
106        let mut acc = row[0];
107        for b in row[1..].iter_mut() {
108            acc = acc.wrapping_add(*b);
109            *b = acc;
110        }
111    } else {
112        for i in factor..n {
113            row[i] = row[i].wrapping_add(row[i - factor]);
114        }
115    }
116    // Assemble samples from the byte planes.
117    match bps_bytes {
118        4 => {
119            let (p0, rest) = row.split_at(samples);
120            let (p1, rest) = rest.split_at(samples);
121            let (p2, p3) = rest.split_at(samples);
122            for ((((o, &a), &b), &c), &d) in out.iter_mut().zip(p0).zip(p1).zip(p2).zip(p3) {
123                *o = f32::from_bits(u32::from_be_bytes([a, b, c, d]));
124            }
125        }
126        3 => {
127            let (p0, rest) = row.split_at(samples);
128            let (p1, p2) = rest.split_at(samples);
129            for (((o, &a), &b), &c) in out.iter_mut().zip(p0).zip(p1).zip(p2) {
130                *o = f24_to_f32((a as u32) << 16 | (b as u32) << 8 | c as u32);
131            }
132        }
133        _ => {
134            let (p0, p1) = row.split_at(samples);
135            for ((o, &a), &b) in out.iter_mut().zip(p0).zip(p1) {
136                *o = f16_to_f32(u16::from_be_bytes([a, b]));
137            }
138        }
139    }
140}
141
142/// Convert half-float bits to f32.
143pub fn f16_to_f32(h: u16) -> f32 {
144    let sign = ((h >> 15) as u32) << 31;
145    let exp = ((h >> 10) & 0x1F) as u32;
146    let mant = (h & 0x3FF) as u32;
147    let bits = if exp == 0 {
148        if mant == 0 {
149            sign
150        } else {
151            // Subnormal: normalise.
152            let mut e = 127 - 15 + 1;
153            let mut m = mant;
154            while m & 0x400 == 0 {
155                m <<= 1;
156                e -= 1;
157            }
158            sign | ((e as u32) << 23) | ((m & 0x3FF) << 13)
159        }
160    } else if exp == 31 {
161        sign | 0x7F80_0000 | (mant << 13)
162    } else {
163        sign | ((exp + 127 - 15) << 23) | (mant << 13)
164    };
165    f32::from_bits(bits)
166}
167
168/// Convert 24-bit DNG float (1/7/16) bits to f32.
169pub fn f24_to_f32(v: u32) -> f32 {
170    let sign = (v >> 23) & 1;
171    let exp = (v >> 16) & 0x7F;
172    let mant = v & 0xFFFF;
173    let bits = if exp == 0 && mant == 0 {
174        sign << 31
175    } else if exp == 0x7F {
176        (sign << 31) | 0x7F80_0000 | (mant << 7)
177    } else {
178        (sign << 31) | ((exp + 127 - 63) << 23) | (mant << 7)
179    };
180    f32::from_bits(bits)
181}
182
183/// Decode big-endian float samples of `bps_bytes` (2, 3 or 4) into `out`.
184pub fn float_bytes_to_f32(src: &[u8], bps_bytes: usize, out: &mut [f32]) {
185    match bps_bytes {
186        2 => {
187            src.chunks_exact(2).zip(out.iter_mut()).for_each(|(c, o)| *o = f16_to_f32(u16::from_be_bytes([c[0], c[1]])))
188        }
189        3 => src
190            .chunks_exact(3)
191            .zip(out.iter_mut())
192            .for_each(|(c, o)| *o = f24_to_f32((c[0] as u32) << 16 | (c[1] as u32) << 8 | c[2] as u32)),
193        _ => src
194            .chunks_exact(4)
195            .zip(out.iter_mut())
196            .for_each(|(c, o)| *o = f32::from_bits(u32::from_be_bytes([c[0], c[1], c[2], c[3]]))),
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn half_floats() {
206        assert_eq!(f16_to_f32(0x3C00), 1.0);
207        assert_eq!(f16_to_f32(0xC000), -2.0);
208        assert_eq!(f16_to_f32(0x0001), 5.960_464_5e-8);
209        assert!(f16_to_f32(0x7C00).is_infinite());
210    }
211
212    #[test]
213    fn f24() {
214        // 1.0 = exp 63, mant 0
215        assert_eq!(f24_to_f32(63 << 16), 1.0);
216        assert_eq!(f24_to_f32((1 << 23) | (64 << 16)), -2.0);
217    }
218
219    #[test]
220    fn horizontal() {
221        let mut r = vec![10u16, 1, 1, 1, 65535];
222        undo_horizontal_u16(&mut r, 1, 1);
223        assert_eq!(r, vec![10, 11, 12, 13, 12]);
224    }
225
226    #[test]
227    fn float_predictor_roundtrip() {
228        let vals = [1.5f32, -2.25, 1000.0, 0.0];
229        // encode: big-endian bytes → byte planes → differences
230        let be: Vec<u8> = vals.iter().flat_map(|v| v.to_be_bytes()).collect();
231        let mut planes = vec![0u8; 16];
232        for s in 0..4 {
233            for b in 0..4 {
234                planes[b * 4 + s] = be[s * 4 + b];
235            }
236        }
237        let mut enc = planes.clone();
238        for i in (1..16).rev() {
239            enc[i] = enc[i].wrapping_sub(enc[i - 1]);
240        }
241        let mut tmp = vec![];
242        undo_float_predictor(&mut enc, &mut tmp, 4, 4, 1);
243        let mut out = [0f32; 4];
244        float_bytes_to_f32(&enc, 4, &mut out);
245        assert_eq!(out, vals);
246    }
247
248    #[test]
249    fn fused_float_row_matches_reference() {
250        let mut x: u32 = 12345;
251        for bps in [2usize, 3, 4] {
252            for factor in [0usize, 1, 2, 4] {
253                for le in [false, true] {
254                    let samples = 37;
255                    let bytes: Vec<u8> = (0..samples * bps)
256                        .map(|_| {
257                            x = x.wrapping_mul(1664525).wrapping_add(1013904223);
258                            (x >> 24) as u8
259                        })
260                        .collect();
261                    // Reference: old two-step path.
262                    let mut r = bytes.clone();
263                    let mut tmp = vec![];
264                    if factor > 0 {
265                        undo_float_predictor(&mut r, &mut tmp, samples, bps, factor);
266                    } else if le {
267                        for c in r.chunks_exact_mut(bps) {
268                            c.reverse();
269                        }
270                    }
271                    let mut want = vec![0f32; samples];
272                    float_bytes_to_f32(&r, bps, &mut want);
273                    let mut got = vec![0f32; samples];
274                    let mut b = bytes.clone();
275                    decode_float_row(&mut b, samples, bps, factor, le, &mut got);
276                    for (g, w) in got.iter().zip(&want) {
277                        assert_eq!(g.to_bits(), w.to_bits(), "bps {bps} factor {factor} le {le}");
278                    }
279                }
280            }
281        }
282    }
283
284    #[test]
285    fn inflate_limits() {
286        assert!(inflate_zlib(&[0x78, 0x9c, 0xff], 100).is_err());
287        assert!(inflate_zlib_into(&[0x78, 0x9c, 0xff], &mut [0u8; 100]).is_err());
288        // "hello" compressed with zlib.
289        let z = [0x78, 0x9c, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00, 0x06, 0x2c, 0x02, 0x15];
290        let mut out = [0u8; 16];
291        assert_eq!(inflate_zlib_into(&z, &mut out).unwrap(), 5);
292        assert_eq!(&out[..5], b"hello");
293        assert_eq!(inflate_zlib(&z, 16).unwrap(), b"hello");
294        assert!(inflate_zlib_into(&z, &mut [0u8; 3]).is_err());
295    }
296}