1use 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#[derive(Debug, Clone, Copy)]
23pub struct Component {
24 pub id: u8,
25 pub h: u8,
26 pub v: u8,
27}
28
29#[derive(Debug, Clone)]
31pub struct LjpegHeader {
32 pub bits: u32,
34 pub width: u32,
36 pub height: u32,
37 pub components: Vec<Component>,
38 pub clrs: u32,
40 pub sraw: u32,
42 pub predictor: u32,
43 pub point_transform: u32,
44 pub restart_interval: u32,
45 pub(crate) tables: Vec<HuffTable>,
47 pub(crate) table_ids: Vec<u8>,
50 pub scan_offset: usize,
52}
53
54#[derive(Debug, Clone, Copy)]
56pub struct LjpegOptions {
57 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 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 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 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 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 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 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 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 pub fn row_len(&self) -> usize {
210 self.width as usize * self.clrs as usize
211 }
212
213 pub fn table(&self, i: usize) -> Option<&HuffTable> {
217 self.tables.get(i)
218 }
219}
220
221pub 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 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
258pub(crate) fn fast_rows(hdr: &LjpegHeader) -> bool {
260 hdr.sraw == 0 && (hdr.predictor == 1 || hdr.height == 1)
261}
262
263#[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 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
289pub struct Unstuffed {
291 pub(crate) data: Vec<u8>,
292 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#[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#[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
324pub fn unstuff(scan: &[u8]) -> Unstuffed {
328 unstuff_scan(scan, true)
329}
330
331pub 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 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
349const PAR_UNSTUFF_MIN: usize = 2 << 20;
351
352impl Unstuffed {
353 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
366fn 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 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 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 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
421fn 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
451fn 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#[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 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#[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 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#[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
617pub 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
628pub 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 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
657pub 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
716pub 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
728pub 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
742pub mod encoder {
745 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 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 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 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 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 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 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 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 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 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 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 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; 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 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 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}