Skip to main content

flashraw_codecs/
ljpeg.rs

1//! Lossless JPEG (ITU T.81 process 14, SOF3) decoder.
2//!
3//! This is the workhorse codec of the RAW world: CR2, most DNGs, Sony lossless ARW,
4//! Hasselblad, Leaf, Kodak, Pentax K-1 DNG, Phase One (some), etc.
5//!
6//! Output model: the frame is decoded row by row into an L1-resident row buffer of
7//! `width * clrs` samples, which is handed to a sink. Callers map JPEG rows to their
8//! final layout (CR2 vertical slices, DNG tile placement, ...) in the sink, so no
9//! full-size intermediate image is ever allocated.
10//!
11//! Canon sRAW/mRAW frames (component 0 with sampling factors > 1) are decoded as
12//! `clrs = ncomp + sraw` interleaved values per MCU, where luma samples chain-predict
13//! from the previous luma sample (matching Canon's encoder).
14
15use flashraw_core::bits::{BitPumpMsb, BitRead};
16use flashraw_core::huffman::HuffTable;
17use flashraw_core::{Error, ReadAt, Result};
18
19pub use crate::ljpeg_par::decode_frame_parallel;
20
21/// Frame component.
22#[derive(Debug, Clone, Copy)]
23pub struct Component {
24    pub id: u8,
25    pub h: u8,
26    pub v: u8,
27}
28
29/// A parsed lossless JPEG stream (headers only; decoding is on demand).
30#[derive(Debug, Clone)]
31pub struct LjpegHeader {
32    /// Sample precision in bits (after point transform).
33    pub bits: u32,
34    /// Frame width in MCUs (samples per line per component).
35    pub width: u32,
36    pub height: u32,
37    pub components: Vec<Component>,
38    /// Values per MCU column: ncomp + extra luma samples for sRAW.
39    pub clrs: u32,
40    /// Number of extra luma samples per MCU (0 = normal frame).
41    pub sraw: u32,
42    pub predictor: u32,
43    pub point_transform: u32,
44    pub restart_interval: u32,
45    /// Huffman table per value position in the MCU.
46    pub(crate) tables: Vec<HuffTable>,
47    /// Table identity per entry of `tables`: equal ids mean identical tables (slots with
48    /// byte-identical definitions share the id of the first).
49    pub(crate) table_ids: Vec<u8>,
50    /// Offset of entropy-coded data in the input.
51    pub scan_offset: usize,
52}
53
54/// Decoder options.
55#[derive(Debug, Clone, Copy)]
56pub struct LjpegOptions {
57    /// Length-16 diffs are `-32768` with no extra bits (true for everything except DNG < 1.1).
58    pub dng_bug16: bool,
59}
60
61impl Default for LjpegOptions {
62    fn default() -> Self {
63        LjpegOptions { dng_bug16: true }
64    }
65}
66
67impl LjpegHeader {
68    /// Parse markers up to and including the first SOS.
69    pub fn parse(data: &[u8], opts: LjpegOptions) -> Result<Self> {
70        if data.len() < 4 || data[0] != 0xFF || data[1] != 0xD8 {
71            return Err(Error::corrupt("lossless JPEG: missing SOI"));
72        }
73        let mut pos = 2usize;
74        let mut dht: [Option<HuffTable>; 4] = [None, None, None, None];
75        // Raw DHT bytes per slot: slots holding identical tables share an id.
76        let mut dht_raw: [Option<&[u8]>; 4] = [None; 4];
77        let mut frame: Option<(u32, u32, u32, Vec<Component>, u32)> = None;
78        let mut restart = 0u32;
79        let mut sof_marker = 0xC3u8;
80        loop {
81            // Skip fill bytes / garbage until a marker.
82            while pos < data.len() && data[pos] != 0xFF {
83                pos += 1;
84            }
85            while pos < data.len() && data[pos] == 0xFF {
86                pos += 1;
87            }
88            let marker = *data.get(pos).ok_or_else(|| Error::corrupt("lossless JPEG: truncated header"))?;
89            pos += 1;
90            if marker == 0xD8 || (0xD0..=0xD7).contains(&marker) || marker == 0x01 {
91                continue;
92            }
93            if marker == 0xD9 {
94                return Err(Error::corrupt("lossless JPEG: EOI before SOS"));
95            }
96            let len = data.u16be(pos)? as usize;
97            if len < 2 {
98                return Err(Error::corrupt("lossless JPEG: invalid segment length"));
99            }
100            let seg = flashraw_core::bytes::slice_at(data, pos + 2, len - 2)?;
101            match marker {
102                0xC3 | 0xC0 | 0xC1 => {
103                    // Some encoders (e.g. Blackmagic) label lossless streams SOF0/SOF1; they are
104                    // accepted and the scan's predictor is verified at SOS.
105                    sof_marker = marker;
106                    if seg.len() < 6 {
107                        return Err(Error::corrupt("lossless JPEG: short SOF"));
108                    }
109                    let bits = seg[0] as u32;
110                    let height = seg.u16be(1)? as u32;
111                    let width = seg.u16be(3)? as u32;
112                    let n = seg[5] as usize;
113                    if n == 0 || n > 4 || seg.len() < 6 + 3 * n {
114                        return Err(Error::corrupt(format!("lossless JPEG: {n} components")));
115                    }
116                    let comps: Vec<Component> = (0..n)
117                        .map(|i| Component { id: seg[6 + 3 * i], h: seg[7 + 3 * i] >> 4, v: seg[7 + 3 * i] & 15 })
118                        .collect();
119                    // Canon sRAW: extra luma samples per MCU from component 0's sampling factors.
120                    let sraw = ((comps[0].h as u32 * comps[0].v as u32).wrapping_sub(1)) & 3;
121                    frame = Some((bits, width, height, comps, sraw));
122                }
123                0xC4 => {
124                    let mut p = 0usize;
125                    while p < seg.len() {
126                        let class_id = seg[p];
127                        let id = (class_id & 0x0F) as usize;
128                        if id > 3 || p + 17 > seg.len() {
129                            return Err(Error::corrupt("lossless JPEG: invalid DHT"));
130                        }
131                        let counts = &seg[p + 1..p + 17];
132                        let total: usize = counts.iter().map(|&c| c as usize).sum();
133                        let vals = flashraw_core::bytes::slice_at(seg, p + 17, total)?;
134                        let mut t = HuffTable::from_counts(counts, vals, true)?;
135                        t.dng_bug16 = opts.dng_bug16;
136                        dht[id] = Some(t);
137                        dht_raw[id] = Some(&seg[p + 1..p + 17 + total]);
138                        p += 17 + total;
139                    }
140                }
141                0xDD => {
142                    restart = seg.u16be(0)? as u32;
143                }
144                0xDA => {
145                    let (bits, width, height, comps, sraw) =
146                        frame.take().ok_or_else(|| Error::corrupt("lossless JPEG: SOS before SOF"))?;
147                    let ns = *seg.first().ok_or_else(|| Error::corrupt("lossless JPEG: short SOS"))? as usize;
148                    if ns != comps.len() || seg.len() < 1 + 2 * ns + 3 {
149                        return Err(Error::corrupt("lossless JPEG: SOS component mismatch"));
150                    }
151                    let mut comp_tables = Vec::with_capacity(ns);
152                    for i in 0..ns {
153                        let td = (seg[2 + 2 * i] >> 4) as usize;
154                        comp_tables.push(td.min(3));
155                    }
156                    let predictor = seg[1 + 2 * ns] as u32;
157                    if sof_marker != 0xC3 && !(1..=7).contains(&predictor) {
158                        return Err(Error::unsupported_compression(
159                            "ljpeg",
160                            format!("JPEG SOF{:X} DCT stream (not lossless)", sof_marker - 0xC0),
161                        ));
162                    }
163                    let pt = (seg[3 + 2 * ns] & 15) as u32;
164                    let clrs = comps.len() as u32 + sraw;
165                    // Table per MCU position: luma table repeated for sRAW extra samples.
166                    let mut tables = Vec::with_capacity(clrs as usize);
167                    let mut table_ids = Vec::with_capacity(clrs as usize);
168                    for c in 0..clrs as usize {
169                        let comp = c.saturating_sub(sraw as usize);
170                        // A missing table falls back to the first one defined.
171                        let id = if dht[comp_tables[comp]].is_some() {
172                            comp_tables[comp]
173                        } else {
174                            dht.iter().position(|t| t.is_some()).unwrap_or(0)
175                        };
176                        let t =
177                            dht[id].clone().ok_or_else(|| Error::corrupt("lossless JPEG: missing Huffman table"))?;
178                        tables.push(t);
179                        let canon = dht_raw.iter().position(|r| r.is_some() && *r == dht_raw[id]).unwrap_or(id);
180                        table_ids.push(canon as u8);
181                    }
182                    if bits == 0 || bits > 16 || pt >= bits || width == 0 || height == 0 {
183                        return Err(Error::corrupt(format!(
184                            "lossless JPEG: invalid frame {width}x{height} @ {bits} bits"
185                        )));
186                    }
187                    return Ok(LjpegHeader {
188                        bits: bits - pt,
189                        width,
190                        height,
191                        components: comps,
192                        clrs,
193                        sraw,
194                        predictor,
195                        point_transform: pt,
196                        restart_interval: restart,
197                        tables,
198                        table_ids,
199                        scan_offset: pos + len,
200                    });
201                }
202                _ => {}
203            }
204            pos += len;
205        }
206    }
207
208    /// Values per decoded row.
209    pub fn row_len(&self) -> usize {
210        self.width as usize * self.clrs as usize
211    }
212
213    /// Huffman table used for value position `i` of an MCU (`i < clrs`). Lets
214    /// vendor codecs with non-standard sample layouts (e.g. Sony YCbCr) reuse the
215    /// parsed stream headers.
216    pub fn table(&self, i: usize) -> Option<&HuffTable> {
217        self.tables.get(i)
218    }
219}
220
221/// Decode all rows of `data` (a complete lossless JPEG stream), invoking
222/// `sink(jpeg_row, samples)` for each row in order.
223pub fn decode_rows<F>(data: &[u8], hdr: &LjpegHeader, mut sink: F) -> Result<()>
224where
225    F: FnMut(usize, &[u16]) -> Result<()>,
226{
227    let rl = hdr.row_len();
228    let mut cur = vec![0u16; rl];
229    let mut prev = vec![0u16; rl];
230    let scan = data.get(hdr.scan_offset..).ok_or_else(|| Error::corrupt("lossless JPEG: scan beyond data"))?;
231    // Remove byte stuffing once, up front: the hot loop then runs on a plain MSB pump
232    // whose refill needs no per-byte 0xFF checks.
233    let un = unstuff_scan(scan, hdr.restart_interval != 0);
234    let mut segment = 0usize;
235    let mut bits = BitPumpMsb::new(un.segment(0));
236    let init = 1u32 << (hdr.bits - 1);
237    let mut vpred = [init as u16; 6];
238    let restart = if hdr.restart_interval > 0 { hdr.restart_interval as usize } else { usize::MAX };
239    let fast = fast_rows(hdr);
240    for jrow in 0..hdr.height as usize {
241        if restart != usize::MAX && (jrow * hdr.width as usize) % restart == 0 {
242            vpred = [init as u16; 6];
243            if jrow > 0 {
244                segment += 1;
245                if segment >= un.segments.len() {
246                    return Err(Error::corrupt("lossless JPEG: missing restart marker"));
247                }
248                bits = BitPumpMsb::new(un.segment(segment));
249            }
250        }
251        decode_row(hdr, fast, &mut bits, &mut cur, &prev, jrow, &mut vpred)?;
252        sink(jrow, &cur)?;
253        std::mem::swap(&mut cur, &mut prev);
254    }
255    Ok(())
256}
257
258/// Rows can use the predictor-1 row decoder.
259pub(crate) fn fast_rows(hdr: &LjpegHeader) -> bool {
260    hdr.sraw == 0 && (hdr.predictor == 1 || hdr.height == 1)
261}
262
263/// Decode row `jrow` into `cur` and check the stream has not run out.
264#[inline(always)]
265pub(crate) fn decode_row(
266    hdr: &LjpegHeader,
267    fast: bool,
268    bits: &mut BitPumpMsb<'_>,
269    cur: &mut [u16],
270    prev: &[u16],
271    jrow: usize,
272    vpred: &mut [u16; 6],
273) -> Result<()> {
274    let clrs = hdr.clrs as usize;
275    if fast {
276        decode_row_pred1(&hdr.tables, bits, cur, clrs, vpred)?;
277    } else {
278        decode_row_generic(hdr, bits, cur, prev, jrow, clrs, vpred)?;
279    }
280    // Up to two bytes consumed past the end are tolerated at the very end (some
281    // encoders drop the final partial byte); anything more means truncation.
282    let slack = if jrow + 1 == hdr.height as usize { 2 } else { 64 };
283    if bits.past_end() > slack {
284        return Err(Error::corrupt(format!("lossless JPEG: data ends at row {jrow}/{}", hdr.height)));
285    }
286    Ok(())
287}
288
289/// Entropy-coded data with `FF 00` stuffing removed, split at restart markers.
290pub struct Unstuffed {
291    pub(crate) data: Vec<u8>,
292    /// `(start, end)` of each restart interval within `data`.
293    pub(crate) segments: Vec<(usize, usize)>,
294}
295
296impl Unstuffed {
297    pub(crate) fn segment(&self, i: usize) -> &[u8] {
298        self.segments.get(i).map(|&(a, b)| &self.data[a..b]).unwrap_or(&[])
299    }
300}
301
302/// Bit mask of the 0xFF bytes of `w` (the high bit of each such byte; bits above the first
303/// hit may be spurious, the lowest one is exact).
304#[inline(always)]
305fn ff_mask(w: u64) -> u64 {
306    let inv = !w;
307    inv.wrapping_sub(0x0101_0101_0101_0101) & !inv & 0x8080_8080_8080_8080
308}
309
310/// Index of the first 0xFF byte (word-at-a-time scan).
311#[cfg(test)]
312fn find_ff(s: &[u8]) -> Option<usize> {
313    let mut i = 0;
314    while i + 8 <= s.len() {
315        let hit = ff_mask(u64::from_le_bytes(s[i..i + 8].try_into().unwrap()));
316        if hit != 0 {
317            return Some(i + (hit.trailing_zeros() / 8) as usize);
318        }
319        i += 8;
320    }
321    s[i..].iter().position(|&b| b == 0xFF).map(|p| i + p)
322}
323
324/// Strip byte stuffing from a JPEG scan. `FF 00` becomes `FF`; `FF D0..D7` (restart)
325/// starts a new segment; any other marker (or a lone trailing `FF`) ends the scan,
326/// exactly where the byte-wise JPEG pump would stop feeding data.
327pub fn unstuff(scan: &[u8]) -> Unstuffed {
328    unstuff_scan(scan, true)
329}
330
331/// [`unstuff`]; without `restarts` a restart marker ends the scan like any other marker
332/// (a decoder reading one restart interval must not pull in the rest of the stream).
333pub fn unstuff_scan(scan: &[u8], restarts: bool) -> Unstuffed {
334    if scan.len() >= PAR_UNSTUFF_MIN && flashraw_core::par::threads() > 1 {
335        return unstuff_parallel(scan, restarts);
336    }
337    unstuff_serial(scan, restarts)
338}
339
340fn unstuff_serial(scan: &[u8], restarts: bool) -> Unstuffed {
341    // Output never outgrows the input; 8 bytes of slack let every step store a whole word.
342    let mut data = vec![0u8; scan.len() + 8];
343    let mut breaks = Vec::new();
344    let (n, _) = unstuff_into(scan, restarts, &mut data, &mut breaks);
345    data.truncate(n);
346    Unstuffed::new(data, breaks)
347}
348
349/// Scans at least this long are unstuffed on all threads.
350const PAR_UNSTUFF_MIN: usize = 2 << 20;
351
352impl Unstuffed {
353    /// From the output and the output positions of restart markers.
354    fn new(data: Vec<u8>, breaks: Vec<usize>) -> Self {
355        let mut segments = Vec::with_capacity(breaks.len() + 1);
356        let mut start = 0;
357        for b in breaks {
358            segments.push((start, b));
359            start = b;
360        }
361        segments.push((start, data.len()));
362        Unstuffed { data, segments }
363    }
364}
365
366/// Unstuff `scan` into `out`, which must hold the whole output (plus 8 bytes for the
367/// fast path to be used up to the end). Pushes the output position of each restart
368/// marker to `breaks`; returns the output length and whether a terminating marker (or
369/// a lone trailing `FF`) was met.
370fn unstuff_into(scan: &[u8], restarts: bool, out: &mut [u8], breaks: &mut Vec<usize>) -> (usize, bool) {
371    let (mut i, mut o) = (0usize, 0usize);
372    loop {
373        // Copy whole words until one holds a 0xFF; the word is stored anyway and the
374        // output cursor advances only past the bytes before the 0xFF.
375        let mut at_ff = false;
376        while i + 8 <= scan.len() && o + 8 <= out.len() {
377            let w = u64::from_le_bytes(scan[i..i + 8].try_into().unwrap());
378            out[o..o + 8].copy_from_slice(&w.to_le_bytes());
379            let hit = ff_mask(w);
380            if hit != 0 {
381                let k = (hit.trailing_zeros() / 8) as usize;
382                i += k;
383                o += k;
384                at_ff = true;
385                break;
386            }
387            i += 8;
388            o += 8;
389        }
390        if !at_ff {
391            // Near the end of the input or of the output.
392            match scan[i..].iter().position(|&b| b == 0xFF) {
393                None => {
394                    let n = scan.len() - i;
395                    out[o..o + n].copy_from_slice(&scan[i..]);
396                    return (o + n, false);
397                }
398                Some(k) => {
399                    out[o..o + k].copy_from_slice(&scan[i..i + k]);
400                    i += k;
401                    o += k;
402                }
403            }
404        }
405        // `scan[i]` is 0xFF.
406        match scan.get(i + 1) {
407            Some(0x00) => {
408                out[o] = 0xFF;
409                o += 1;
410                i += 2;
411            }
412            Some(&m) if restarts && (0xD0..=0xD7).contains(&m) => {
413                breaks.push(o);
414                i += 2;
415            }
416            _ => return (o, true),
417        }
418    }
419}
420
421/// Output length of [`unstuff_into`] and whether the scan terminates.
422fn unstuffed_len(scan: &[u8], restarts: bool) -> (usize, bool) {
423    let mut removed = 0usize;
424    let mut i = 0usize;
425    loop {
426        let mut at_ff = false;
427        while i + 8 <= scan.len() {
428            let hit = ff_mask(u64::from_le_bytes(scan[i..i + 8].try_into().unwrap()));
429            if hit != 0 {
430                i += (hit.trailing_zeros() / 8) as usize;
431                at_ff = true;
432                break;
433            }
434            i += 8;
435        }
436        if !at_ff {
437            match scan[i..].iter().position(|&b| b == 0xFF) {
438                None => return (scan.len() - removed, false),
439                Some(k) => i += k,
440            }
441        }
442        match scan.get(i + 1) {
443            Some(0x00) => removed += 1,
444            Some(&m) if restarts && (0xD0..=0xD7).contains(&m) => removed += 2,
445            _ => return (i - removed, true),
446        }
447        i += 2;
448    }
449}
450
451/// [`unstuff_scan`] on all threads. Every `FF` starts a two-byte sequence, so the scan
452/// splits into chunks that do not start right after an `FF`, and each chunk unstuffs
453/// on its own: lengths first, then each chunk writes its own part of the output. The
454/// output is that of the first chunks up to the one that terminates.
455fn unstuff_parallel(scan: &[u8], restarts: bool) -> Unstuffed {
456    use flashraw_core::par;
457    let n = par::threads() * 2;
458    let mut cuts = vec![0usize];
459    for k in 1..n {
460        let mut b = (scan.len() * k / n).max(*cuts.last().unwrap() + 1);
461        while b < scan.len() && scan[b - 1] == 0xFF {
462            b += 1;
463        }
464        if b < scan.len() {
465            cuts.push(b);
466        }
467    }
468    cuts.push(scan.len());
469    let chunks: Vec<&[u8]> = cuts.windows(2).map(|w| &scan[w[0]..w[1]]).collect();
470    let lens = par::map(chunks.clone(), true, |c| unstuffed_len(c, restarts));
471    let used = lens.iter().position(|&(_, end)| end).map_or(lens.len(), |k| k + 1);
472    let mut bounds = Vec::with_capacity(used);
473    let mut total = 0usize;
474    for &(len, _) in &lens[..used] {
475        bounds.push((total, total + len));
476        total += len;
477    }
478    let mut data = vec![0u8; total];
479    let Some(parts) = par::split_disjoint(&mut data, &bounds) else { return unstuff_serial(scan, restarts) };
480    let jobs: Vec<(&[u8], &mut [u8], usize)> =
481        chunks.into_iter().zip(parts).zip(&bounds).map(|((c, p), &(start, _))| (c, p, start)).collect();
482    let breaks = par::map(jobs, true, |(c, p, start)| {
483        let mut b = Vec::new();
484        unstuff_into(c, restarts, p, &mut b);
485        b.into_iter().map(|x| x + start).collect::<Vec<_>>()
486    });
487    Unstuffed::new(data, breaks.into_iter().flatten().collect())
488}
489
490/// Predictor 1 (left neighbour), the overwhelmingly common case.
491#[inline]
492fn decode_row_pred1<B: BitRead>(
493    tables: &[HuffTable],
494    bits: &mut B,
495    cur: &mut [u16],
496    clrs: usize,
497    vpred: &mut [u16; 6],
498) -> Result<()> {
499    // First MCU predicts from the first MCU of the previous row (vpred accumulates).
500    for c in 0..clrs {
501        let diff = tables[c].decode_diff(bits)?;
502        vpred[c] = (vpred[c] as i32 + diff) as u16;
503        cur[c] = vpred[c];
504    }
505    match clrs {
506        1 => {
507            let t = &tables[0];
508            let mut p = cur[0] as i32;
509            for v in cur[1..].iter_mut() {
510                p = (p + t.decode_diff(bits)?) as u16 as i32;
511                *v = p as u16;
512            }
513        }
514        2 => decode_interleaved::<B, 2>(tables, bits, cur)?,
515        3 => decode_interleaved::<B, 3>(tables, bits, cur)?,
516        4 => decode_interleaved::<B, 4>(tables, bits, cur)?,
517        _ => {
518            for i in clrs..cur.len() {
519                let c = i % clrs;
520                let diff = tables[c].decode_diff(bits)?;
521                cur[i] = (cur[i - clrs] as i32 + diff) as u16;
522            }
523        }
524    }
525    Ok(())
526}
527
528/// Predictor-1 decode of `N` interleaved components (CR2 uses 2 or 4): predictors stay in
529/// registers and there is no per-sample component index arithmetic.
530#[inline(always)]
531fn decode_interleaved<B: BitRead, const N: usize>(tables: &[HuffTable], bits: &mut B, cur: &mut [u16]) -> Result<()> {
532    let t: [&HuffTable; N] = std::array::from_fn(|c| &tables[c]);
533    let mut p: [u16; N] = std::array::from_fn(|c| cur[c]);
534    let mut err = false;
535    for px in cur[N..].chunks_exact_mut(N) {
536        // One refill (>= 56 bits for byte pumps) covers two table-hit samples (<= 27 bits
537        // each); the odd-sample check only triggers for pumps with coarser refills.
538        for c in 0..N {
539            bits.fill_to(if c % 2 == 0 { 54 } else { 27 });
540            p[c] = (p[c] as i32 + t[c].decode_diff_buffered(bits, &mut err)) as u16;
541            px[c] = p[c];
542        }
543    }
544    if err {
545        return Err(Error::corrupt("lossless JPEG: invalid Huffman code"));
546    }
547    Ok(())
548}
549
550#[inline(never)]
551fn decode_row_generic<B: BitRead>(
552    hdr: &LjpegHeader,
553    bits: &mut B,
554    cur: &mut [u16],
555    prev: &[u16],
556    jrow: usize,
557    clrs: usize,
558    vpred: &mut [u16; 6],
559) -> Result<()> {
560    reconstruct_row(hdr, |c| hdr.tables[c].decode_diff(bits), cur, prev, jrow, clrs, vpred)
561}
562
563/// Rebuild one row from its differences (`next(c)` yields the next difference, `c` being
564/// the MCU position): every predictor, sRAW sample layouts, and the vertical chain of
565/// first-column predictors kept in `vpred`.
566#[inline(always)]
567pub(crate) fn reconstruct_row(
568    hdr: &LjpegHeader,
569    mut next: impl FnMut(usize) -> Result<i32>,
570    cur: &mut [u16],
571    prev: &[u16],
572    jrow: usize,
573    clrs: usize,
574    vpred: &mut [u16; 6],
575) -> Result<()> {
576    let sraw = hdr.sraw as usize;
577    let psv = hdr.predictor;
578    let mut spred: i32 = 0;
579    let width = hdr.width as usize;
580    for col in 0..width {
581        for c in 0..clrs {
582            let i = col * clrs + c;
583            let diff = next(c)?;
584            let mut pred: i32 = if sraw > 0 && c <= sraw && (col | c) != 0 {
585                spred
586            } else if col > 0 {
587                cur[i - clrs] as i32
588            } else {
589                let p = vpred[c] as i32;
590                vpred[c] = (p + diff) as u16;
591                p
592            };
593            if jrow > 0 && col > 0 {
594                let rb = prev[i] as i32;
595                let rc = prev[i - clrs] as i32;
596                pred = match psv {
597                    1 => pred,
598                    2 => rb,
599                    3 => rc,
600                    4 => pred + rb - rc,
601                    5 => pred + ((rb - rc) >> 1),
602                    6 => rb + ((pred - rc) >> 1),
603                    7 => (pred + rb) >> 1,
604                    _ => 0,
605                };
606            }
607            let v = (pred + diff) as u16;
608            cur[i] = v;
609            if c <= sraw {
610                spred = v as i32;
611            }
612        }
613    }
614    Ok(())
615}
616
617/// [`decode_rows`] that first decodes large scans on all threads when `parallel` is set
618/// ([`decode_frame_parallel`]; scans with restart markers one interval per task). Rows reach `sink` in order either
619/// way with identical values; corrupt streams always take the sequential path, so errors
620/// are identical too.
621pub fn decode_rows_par<F>(data: &[u8], hdr: &LjpegHeader, parallel: bool, sink: F) -> Result<()>
622where
623    F: FnMut(usize, &[u16]) -> Result<()>,
624{
625    decode_rows_par_limit(data, hdr, parallel, hdr.height as usize, sink)
626}
627
628/// [`decode_rows_par`] for callers that only need the first `rows` rows and ignore what
629/// the stream holds after them (the parallel path then stops there; the sequential one
630/// still delivers every row).
631pub fn decode_rows_par_limit<F>(data: &[u8], hdr: &LjpegHeader, parallel: bool, rows: usize, mut sink: F) -> Result<()>
632where
633    F: FnMut(usize, &[u16]) -> Result<()>,
634{
635    let rl = hdr.row_len();
636    let rows = rows.min(hdr.height as usize);
637    let total = rl * rows;
638    // Restart intervals are decoded independently, without speculation, so they pay off
639    // on smaller frames.
640    let min = if hdr.restart_interval != 0 {
641        flashraw_core::par::PAR_MIN_SAMPLES
642    } else {
643        crate::ljpeg_par::LJPEG_PARALLEL_MIN_SAMPLES
644    };
645    if parallel && total >= min {
646        let mut frame = vec![0u16; total];
647        if crate::ljpeg_par::decode_frame_rows(data, hdr, rows, &mut frame)? {
648            for (jrow, row) in frame.chunks_exact(rl).enumerate() {
649                sink(jrow, row)?;
650            }
651            return Ok(());
652        }
653    }
654    decode_rows(data, hdr, sink)
655}
656
657/// Decode a lossless JPEG tile into a destination buffer.
658///
659/// The decoded sample stream fills a virtual tile whose rows are `tile_row_len`
660/// samples long (JPEG rows need not match tile rows: DNG commonly encodes a
661/// `W`-pixel tile row as a `W/2 x 2-component` JPEG row, and some encoders put two
662/// tile rows in one JPEG row). Only samples with column `< clip_w` and row
663/// `< clip_h` are written, at `dst[row * dst_stride + col]` — this clips tiles
664/// that overhang the image edge.
665pub fn decode_tile(
666    data: &[u8],
667    opts: LjpegOptions,
668    dst: &mut [u16],
669    dst_stride: usize,
670    tile_row_len: usize,
671    clip_w: usize,
672    clip_h: usize,
673) -> Result<LjpegHeader> {
674    decode_tile_impl(data, opts, dst, dst_stride, tile_row_len, clip_w, clip_h, false)
675}
676
677#[allow(clippy::too_many_arguments)]
678fn decode_tile_impl(
679    data: &[u8],
680    opts: LjpegOptions,
681    dst: &mut [u16],
682    dst_stride: usize,
683    tile_row_len: usize,
684    clip_w: usize,
685    clip_h: usize,
686    parallel: bool,
687) -> Result<LjpegHeader> {
688    let hdr = LjpegHeader::parse(data, opts)?;
689    if tile_row_len == 0 || clip_w == 0 || clip_h == 0 {
690        return Ok(hdr);
691    }
692    let clip_w = clip_w.min(tile_row_len).min(dst_stride);
693    let (mut row, mut col) = (0usize, 0usize);
694    decode_rows_par(data, &hdr, parallel, |_, mut src| {
695        while !src.is_empty() && row < clip_h {
696            let take = (tile_row_len - col).min(src.len());
697            if col < clip_w {
698                let n = (clip_w - col).min(take);
699                let off = row * dst_stride + col;
700                if let Some(d) = dst.get_mut(off..off + n) {
701                    d.copy_from_slice(&src[..n]);
702                }
703            }
704            src = &src[take..];
705            col += take;
706            if col == tile_row_len {
707                col = 0;
708                row += 1;
709            }
710        }
711        Ok(())
712    })?;
713    Ok(hdr)
714}
715
716/// Decode a lossless JPEG stream whose rows map 1:1 onto `width`-sample image rows.
717pub fn decode_into(
718    data: &[u8],
719    opts: LjpegOptions,
720    dst: &mut [u16],
721    dst_stride: usize,
722    width: usize,
723    height: usize,
724) -> Result<LjpegHeader> {
725    decode_tile(data, opts, dst, dst_stride, width, width, height)
726}
727
728/// [`decode_into`] that may decode large restart-free streams on all threads
729/// (see [`decode_rows_par`]).
730pub fn decode_into_par(
731    data: &[u8],
732    opts: LjpegOptions,
733    dst: &mut [u16],
734    dst_stride: usize,
735    width: usize,
736    height: usize,
737    parallel: bool,
738) -> Result<LjpegHeader> {
739    decode_tile_impl(data, opts, dst, dst_stride, width, width, height, parallel)
740}
741
742/// Minimal lossless JPEG encoder, used to build synthetic fixtures for tests,
743/// fuzzing seeds and malformed-input suites (not optimised).
744pub mod encoder {
745    /// Encode `samples` (`height` rows of `width * comps` interleaved values) as a
746    /// lossless JPEG with predictor `psv` (1..=7) and a fixed Huffman table.
747    pub fn encode(width: usize, height: usize, comps: usize, bits: u32, psv: u32, samples: &[u16]) -> Vec<u8> {
748        encode_with_restarts(width, height, comps, bits, psv, samples, 0)
749    }
750
751    /// [`encode`] with a restart interval of `restart` MCUs (0: none). Intervals must
752    /// start at row starts; each one resets the first-column predictors, and other
753    /// predictors keep reading the row above (as the decoder does).
754    pub fn encode_with_restarts(
755        width: usize,
756        height: usize,
757        comps: usize,
758        bits: u32,
759        psv: u32,
760        samples: &[u16],
761        restart: usize,
762    ) -> Vec<u8> {
763        // Huffman table: lengths 0..=16 with simple code lengths.
764        let counts: [u8; 16] = [0, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0];
765        let values: Vec<u8> = (0..=16).collect();
766        let mut codes = [(0u32, 0u32); 17];
767        let mut code = 0u32;
768        let mut k = 0;
769        for len in 1..=16u32 {
770            for _ in 0..counts[len as usize - 1] {
771                codes[values[k] as usize] = (code, len);
772                code += 1;
773                k += 1;
774            }
775            code <<= 1;
776        }
777        let mut out = vec![0xFF, 0xD8];
778        // SOF3
779        out.extend([0xFF, 0xC3]);
780        out.extend(((8 + 3 * comps) as u16).to_be_bytes());
781        out.push(bits as u8);
782        out.extend((height as u16).to_be_bytes());
783        out.extend((width as u16).to_be_bytes());
784        out.push(comps as u8);
785        for c in 0..comps {
786            out.extend([c as u8 + 1, 0x11, 0]);
787        }
788        // DHT
789        out.extend([0xFF, 0xC4]);
790        out.extend(((2 + 17 + values.len()) as u16).to_be_bytes());
791        out.push(0x00);
792        out.extend(counts);
793        out.extend(&values);
794        if restart > 0 {
795            out.extend([0xFF, 0xDD, 0, 4]);
796            out.extend((restart as u16).to_be_bytes());
797        }
798        // SOS
799        out.extend([0xFF, 0xDA]);
800        out.extend(((6 + 2 * comps) as u16).to_be_bytes());
801        out.push(comps as u8);
802        for c in 0..comps {
803            out.extend([c as u8 + 1, 0x00]);
804        }
805        out.extend([psv as u8, 0, 0]);
806        // Entropy data
807        // Bit accumulator and its fill.
808        let mut st = (0u64, 0u32);
809        let mut ent = vec![];
810        let put = |v: u32, len: u32, ent: &mut Vec<u8>, (acc, n): &mut (u64, u32)| {
811            for i in (0..len).rev() {
812                *acc = (*acc << 1) | ((v >> i) & 1) as u64;
813                *n += 1;
814                if *n == 8 {
815                    ent.push(*acc as u8);
816                    if *acc as u8 == 0xFF {
817                        ent.push(0);
818                    }
819                    *acc = 0;
820                    *n = 0;
821                }
822            }
823        };
824        let rl = width * comps;
825        let s = |r: usize, i: usize| samples[r * rl + i] as i32;
826        let mut markers = 0u8;
827        for r in 0..height {
828            let restarted = restart > 0 && (r * width) % restart == 0;
829            if restarted && r > 0 {
830                // Pad the partial byte with 1-bits and emit RSTn.
831                if st.1 > 0 {
832                    put(0x7F, 8 - st.1, &mut ent, &mut st);
833                }
834                ent.extend([0xFF, 0xD0 + markers % 8]);
835                markers += 1;
836            }
837            for col in 0..width {
838                for c in 0..comps {
839                    let i = col * comps + c;
840                    let pred = if (r == 0 || restarted) && col == 0 {
841                        1 << (bits - 1)
842                    } else if r == 0 {
843                        s(r, i - comps)
844                    } else if col == 0 {
845                        s(r - 1, i)
846                    } else {
847                        let (ra, rb, rc) = (s(r, i - comps), s(r - 1, i), s(r - 1, i - comps));
848                        match psv {
849                            1 => ra,
850                            2 => rb,
851                            3 => rc,
852                            4 => ra + rb - rc,
853                            5 => ra + ((rb - rc) >> 1),
854                            6 => rb + ((ra - rc) >> 1),
855                            _ => (ra + rb) >> 1,
856                        }
857                    };
858                    let mut diff = (s(r, i) - pred) as i16 as i32;
859                    if diff == -32768 {
860                        let (c16, l16) = codes[16];
861                        put(c16, l16, &mut ent, &mut st);
862                        continue;
863                    }
864                    let len = if diff == 0 { 0 } else { 32 - diff.unsigned_abs().leading_zeros() };
865                    let (cd, l) = codes[len as usize];
866                    put(cd, l, &mut ent, &mut st);
867                    if len > 0 {
868                        if diff < 0 {
869                            diff -= 1;
870                        }
871                        put(diff as u32 & ((1 << len) - 1), len, &mut ent, &mut st);
872                    }
873                }
874            }
875        }
876        // Pad the final partial byte with 1-bits (JPEG convention).
877        put(0x7F, 7, &mut ent, &mut st);
878        out.extend(ent);
879        out.extend([0xFF, 0xD9]);
880        out
881    }
882}
883
884#[cfg(test)]
885pub(crate) mod tests {
886    use super::encoder::encode;
887    use super::*;
888
889    fn pseudo(n: usize, bits: u32, seed: u32) -> Vec<u16> {
890        let mut x = seed;
891        let mut v = 1000i32;
892        (0..n)
893            .map(|_| {
894                x = x.wrapping_mul(1664525).wrapping_add(1013904223);
895                v = (v + ((x >> 16) as i32 % 200) - 100).clamp(0, (1 << bits) - 1);
896                if x % 97 == 0 { ((x >> 8) % (1 << bits)) as u16 } else { v as u16 }
897            })
898            .collect()
899    }
900
901    #[test]
902    fn roundtrip_all_predictors() {
903        for psv in 1..=7 {
904            for comps in 1..=4 {
905                let (w, h) = (37, 11);
906                let s = pseudo(w * h * comps, 14, psv * 31 + comps as u32);
907                let jpg = encode(w, h, comps, 14, psv, &s);
908                let hdr = LjpegHeader::parse(&jpg, LjpegOptions::default()).unwrap();
909                let mut out = vec![];
910                decode_rows(&jpg, &hdr, |_, r| {
911                    out.extend_from_slice(r);
912                    Ok(())
913                })
914                .unwrap();
915                assert_eq!(out, s, "psv {psv} comps {comps}");
916            }
917        }
918    }
919
920    #[test]
921    fn unstuffing_stops_at_restart_without_restarts() {
922        // One restart interval read on its own must not pull in the following ones.
923        let scan = [1u8, 0xFF, 0x00, 2, 0xFF, 0xD0, 3, 4, 0xFF, 0xD1, 5];
924        let un = unstuff_scan(&scan, false);
925        assert_eq!(un.segments.len(), 1);
926        assert_eq!(un.segment(0), &[1, 0xFF, 2]);
927        assert_eq!(unstuff_scan(&scan, true).segments.len(), 3);
928    }
929
930    #[test]
931    fn unstuffing_and_restart_segments() {
932        let scan = [0x12, 0xFF, 0x00, 0x34, 0xFF, 0xD0, 0x56, 0xFF, 0x00, 0xFF, 0xD1, 0x78, 0xFF, 0xD9, 0x99];
933        let u = unstuff(&scan);
934        assert_eq!(u.segment(0), &[0x12, 0xFF, 0x34]);
935        assert_eq!(u.segment(1), &[0x56, 0xFF]);
936        assert_eq!(u.segment(2), &[0x78]);
937        assert_eq!(u.segments.len(), 3);
938        // Long runs without 0xFF go through the word-at-a-time scanner.
939        let long: Vec<u8> = (0..1000u32).map(|i| (i % 255) as u8).collect();
940        assert_eq!(unstuff(&long).segment(0), &long[..]);
941        assert_eq!(find_ff(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF]), Some(9));
942        assert_eq!(find_ff(&[0xFF]), Some(0));
943        assert_eq!(find_ff(&[1, 2]), None);
944    }
945
946    /// Byte-at-a-time reference for [`unstuff_scan`].
947    fn unstuff_reference(scan: &[u8], restarts: bool) -> (Vec<u8>, Vec<(usize, usize)>) {
948        let (mut data, mut segs, mut start, mut i) = (Vec::new(), Vec::new(), 0, 0);
949        while i < scan.len() {
950            if scan[i] != 0xFF {
951                data.push(scan[i]);
952                i += 1;
953                continue;
954            }
955            match scan.get(i + 1) {
956                Some(0) => data.push(0xFF),
957                Some(&m) if restarts && (0xD0..=0xD7).contains(&m) => {
958                    segs.push((start, data.len()));
959                    start = data.len();
960                }
961                _ => break,
962            }
963            i += 2;
964        }
965        segs.push((start, data.len()));
966        (data, segs)
967    }
968
969    #[test]
970    fn unstuffing_matches_bytewise_reference() {
971        let mut x = 7u32;
972        for case in 0..600 {
973            let len = case * 3 % 97 + (case % 5) * 40 + if case >= 400 { 5000 } else { 0 };
974            let dense = case % 3; // how often 0xFF appears
975            let scan: Vec<u8> = (0..len)
976                .map(|_| {
977                    x = x.wrapping_mul(1664525).wrapping_add(1013904223);
978                    let r = (x >> 24) as u8;
979                    match (dense, r % 16) {
980                        (0, _) => r,
981                        (_, 0..=3) => 0xFF,
982                        (2, 4..=7) => 0x00,
983                        (_, 8) => 0xD0 + r % 8,
984                        (_, 9) if case % 7 == 0 => 0xD9,
985                        _ => r,
986                    }
987                })
988                .collect();
989            for restarts in [false, true] {
990                let u = unstuff_scan(&scan, restarts);
991                let (data, segs) = unstuff_reference(&scan, restarts);
992                assert_eq!(u.data, data, "case {case} restarts {restarts}");
993                assert_eq!(u.segments, segs, "case {case} restarts {restarts}");
994                let p = unstuff_parallel(&scan, restarts);
995                assert_eq!(p.data, data, "parallel: case {case} restarts {restarts}");
996                assert_eq!(p.segments, segs, "parallel: case {case} restarts {restarts}");
997                assert_eq!(unstuffed_len(&scan, restarts).0, data.len());
998            }
999        }
1000    }
1001
1002    #[test]
1003    fn full_16_bit_range() {
1004        let (w, h) = (16, 4);
1005        let mut s = pseudo(w * h, 16, 7);
1006        s[5] = 0;
1007        s[6] = 65535;
1008        s[7] = 0;
1009        let jpg = encode(w, h, 1, 16, 1, &s);
1010        let mut out = vec![0u16; w * h];
1011        decode_into(&jpg, LjpegOptions::default(), &mut out, w, w, h).unwrap();
1012        assert_eq!(out, s);
1013    }
1014
1015    #[test]
1016    fn tile_mapping_two_component_rows_and_clipping() {
1017        // A 8x4 tile encoded as a 4-wide, 2-component JPEG; clip to 6x3 into a stride-10 buffer.
1018        let (w, h) = (8usize, 4usize);
1019        let s = pseudo(w * h, 12, 11);
1020        let jpg = encode(w / 2, h, 2, 12, 1, &s);
1021        let mut out = vec![0u16; 10 * 3];
1022        decode_tile(&jpg, LjpegOptions::default(), &mut out, 10, w, 6, 3).unwrap();
1023        for y in 0..3 {
1024            assert_eq!(&out[y * 10..y * 10 + 6], &s[y * w..y * w + 6]);
1025            assert_eq!(&out[y * 10 + 6..y * 10 + 10], &[0, 0, 0, 0]);
1026        }
1027        // Two tile rows per JPEG row.
1028        let jpg = encode(w * 2, h / 2, 1, 12, 1, &s);
1029        let mut out = vec![0u16; w * h];
1030        decode_tile(&jpg, LjpegOptions::default(), &mut out, w, w, w, h).unwrap();
1031        assert_eq!(out, s);
1032    }
1033
1034    #[test]
1035    fn truncated_stream_errors() {
1036        let (w, h) = (64, 64);
1037        let s = pseudo(w * h, 12, 3);
1038        let jpg = encode(w, h, 1, 12, 1, &s);
1039        for cut in [jpg.len() / 4, jpg.len() / 2] {
1040            let r = decode_into(&jpg[..cut], LjpegOptions::default(), &mut vec![0; w * h], w, w, h);
1041            assert!(r.is_err());
1042        }
1043    }
1044}