1#[derive(Debug, Clone)]
24pub struct Image {
25 pub width: usize,
26 pub height: usize,
27 pub channels: usize,
28 pub data: Vec<u8>,
29 pub adobe_inverted: bool,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum Error {
37 Unsupported(&'static str),
38 Corrupt(&'static str),
39}
40
41#[derive(Debug, Clone, Copy)]
43pub struct Info {
44 pub width: usize,
45 pub height: usize,
46 pub components: usize,
47}
48
49const ZIGZAG: [usize; 64] = [
50 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
51 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
53];
54
55#[derive(Clone, Default)]
56struct Huffman {
57 maxcode: [i32; 18],
61 valptr: [i32; 17],
62 mincode: [i32; 17],
63 values: Vec<u8>,
64 present: bool,
65}
66
67impl Huffman {
68 fn build(bits: &[u8; 16], values: Vec<u8>) -> Result<Self, Error> {
69 let mut h = Huffman {
70 values,
71 present: true,
72 ..Default::default()
73 };
74 let mut code: i32 = 0;
75 let mut k: i32 = 0;
76 for l in 1..=16usize {
77 let n = i32::from(bits[l - 1]);
78 if n == 0 {
79 h.maxcode[l] = -1;
80 } else {
81 h.valptr[l] = k;
82 h.mincode[l] = code;
83 code += n;
84 k += n;
85 h.maxcode[l] = code - 1;
86 }
87 code <<= 1;
88 }
89 h.maxcode[17] = i32::MAX;
90 if k as usize != h.values.len() {
91 return Err(Error::Corrupt("DHT value count"));
92 }
93 Ok(h)
94 }
95}
96
97struct Reader<'a> {
98 data: &'a [u8],
99 pos: usize,
100 acc: u64,
101 nbits: u32,
102 marker: Option<u8>,
105}
106
107impl<'a> Reader<'a> {
108 fn new(data: &'a [u8], pos: usize) -> Self {
109 Reader {
110 data,
111 pos,
112 acc: 0,
113 nbits: 0,
114 marker: None,
115 }
116 }
117
118 fn fill(&mut self) {
119 while self.nbits <= 56 {
120 let byte = if self.marker.is_some() {
121 0
122 } else {
123 match self.data.get(self.pos) {
124 None => {
125 self.marker = Some(0xD9);
126 0
127 }
128 Some(&0xFF) => {
129 let next = self.data.get(self.pos + 1).copied().unwrap_or(0xD9);
130 if next == 0 {
131 self.pos += 2;
132 0xFF
133 } else if next == 0xFF {
134 self.pos += 1;
136 continue;
137 } else {
138 self.marker = Some(next);
139 0
140 }
141 }
142 Some(&b) => {
143 self.pos += 1;
144 b
145 }
146 }
147 };
148 self.acc |= u64::from(byte) << (56 - self.nbits);
149 self.nbits += 8;
150 }
151 }
152
153 fn bits(&mut self, n: u32) -> u32 {
154 if n == 0 {
155 return 0;
156 }
157 if self.nbits < n {
158 self.fill();
159 }
160 let v = (self.acc >> (64 - n)) as u32;
161 self.acc <<= n;
162 self.nbits -= n;
163 v
164 }
165
166 fn bit(&mut self) -> u32 {
167 self.bits(1)
168 }
169
170 fn receive_extend(&mut self, s: u32) -> i32 {
172 if s == 0 {
173 return 0;
174 }
175 let v = self.bits(s) as i32;
176 if v < (1 << (s - 1)) {
177 v - (1 << s) + 1
178 } else {
179 v
180 }
181 }
182
183 fn decode(&mut self, h: &Huffman) -> Result<u8, Error> {
184 if !h.present {
185 return Err(Error::Corrupt("missing Huffman table"));
186 }
187 let mut code = self.bit() as i32;
188 let mut l = 1usize;
189 while code > h.maxcode[l] {
190 code = (code << 1) | self.bit() as i32;
191 l += 1;
192 if l > 16 {
193 return Ok(0);
195 }
196 }
197 let idx = h.valptr[l] + code - h.mincode[l];
198 Ok(h.values.get(idx as usize).copied().unwrap_or(0))
199 }
200
201 fn reset(&mut self) {
203 self.acc = 0;
204 self.nbits = 0;
205 self.marker = None;
206 }
207
208 fn marker_position(&self) -> usize {
210 if self.marker.is_some() {
213 return self.pos;
214 }
215 let mut p = self.pos;
216 while p + 1 < self.data.len() {
217 if self.data[p] == 0xFF && self.data[p + 1] != 0 && self.data[p + 1] != 0xFF {
218 return p;
219 }
220 p += 1;
221 }
222 self.data.len()
223 }
224}
225
226struct Component {
227 id: u8,
228 h: usize,
229 v: usize,
230 tq: usize,
231 bw: usize,
233 bh: usize,
234 dw: usize,
237 dh: usize,
238 dct: usize,
242 coefs: Vec<i16>,
245 samples: Vec<u8>,
247 dc_tbl: usize,
248 ac_tbl: usize,
249 dc_pred: i32,
250}
251
252#[derive(Clone, Copy, PartialEq, Eq, Debug)]
253enum ColorSpace {
254 Gray,
255 YCbCr,
256 Rgb,
257}
258
259struct Decoder<'a> {
260 data: &'a [u8],
261 qt: [[u16; 64]; 4],
262 qt_present: [bool; 4],
263 dc: [Huffman; 4],
264 ac: [Huffman; 4],
265 comps: Vec<Component>,
266 width: usize,
267 height: usize,
268 hmax: usize,
269 vmax: usize,
270 mcux: usize,
271 mcuy: usize,
272 progressive: bool,
273 restart_interval: usize,
274 saw_jfif: bool,
275 adobe_transform: Option<u8>,
276 eobrun: u32,
277 min_dct: usize,
279}
280
281pub fn info(data: &[u8]) -> Result<Info, Error> {
283 let mut d = Decoder::new(data, 1)?;
284 d.run(true)?;
285 Ok(Info {
286 width: d.width,
287 height: d.height,
288 components: d.comps.len(),
289 })
290}
291
292pub fn decode(data: &[u8], color_transform: bool, scale_denom: u32) -> Result<Image, Error> {
300 let mut d = Decoder::new(data, scale_denom)?;
301 d.run(false)?;
302 d.finish(color_transform)
303}
304
305impl<'a> Decoder<'a> {
306 fn new(data: &'a [u8], scale_denom: u32) -> Result<Self, Error> {
307 let min_dct = match scale_denom {
309 1 => 8,
310 2 => 4,
311 4 => 2,
312 8 => 1,
313 _ => return Err(Error::Unsupported("DCT scale")),
314 };
315 Ok(Decoder {
316 data,
317 qt: [[0; 64]; 4],
318 qt_present: [false; 4],
319 dc: Default::default(),
320 ac: Default::default(),
321 comps: Vec::new(),
322 width: 0,
323 height: 0,
324 hmax: 1,
325 vmax: 1,
326 mcux: 0,
327 mcuy: 0,
328 progressive: false,
329 restart_interval: 0,
330 saw_jfif: false,
331 adobe_transform: None,
332 eobrun: 0,
333 min_dct,
334 })
335 }
336
337 fn u16_at(&self, p: usize) -> Result<usize, Error> {
338 match (self.data.get(p), self.data.get(p + 1)) {
339 (Some(&a), Some(&b)) => Ok(usize::from(a) << 8 | usize::from(b)),
340 _ => Err(Error::Corrupt("truncated")),
341 }
342 }
343
344 fn run(&mut self, header_only: bool) -> Result<(), Error> {
346 let mut p = 0usize;
347 while p + 1 < self.data.len() && !(self.data[p] == 0xFF && self.data[p + 1] == 0xD8) {
350 p += 1;
351 }
352 if p + 1 >= self.data.len() {
353 return Err(Error::Corrupt("no SOI"));
354 }
355 p += 2;
356 loop {
357 while p < self.data.len() && self.data[p] != 0xFF {
359 p += 1;
360 }
361 while p < self.data.len() && self.data[p] == 0xFF {
362 p += 1;
363 }
364 let Some(&marker) = self.data.get(p) else {
365 return if self.comps.is_empty() {
366 Err(Error::Corrupt("no frame"))
367 } else {
368 Ok(())
369 };
370 };
371 p += 1;
372 match marker {
373 0xD8 | 0x01 | 0xD0..=0xD7 => continue, 0xD9 => return Ok(()), _ => {}
376 }
377 let len = self.u16_at(p)?;
378 if len < 2 {
379 return Err(Error::Corrupt("segment length"));
380 }
381 let seg = self
382 .data
383 .get(p + 2..p + len)
384 .ok_or(Error::Corrupt("truncated segment"))?;
385 match marker {
386 0xC0..=0xC2 => {
387 self.progressive = marker == 0xC2;
388 self.frame(seg)?;
389 }
390 0xC3 | 0xC5..=0xC7 | 0xC9..=0xCB | 0xCD..=0xCF => {
391 return Err(Error::Unsupported(
392 "lossless, hierarchical or arithmetic JPEG",
393 ));
394 }
395 0xC4 => self.dht(seg)?,
396 0xDB => self.dqt(seg)?,
397 0xDD => {
398 if seg.len() < 2 {
399 return Err(Error::Corrupt("DRI"));
400 }
401 self.restart_interval = usize::from(seg[0]) << 8 | usize::from(seg[1]);
402 }
403 0xDC => return Err(Error::Unsupported("DNL")),
404 0xE0 => {
405 if seg.starts_with(b"JFIF\0") {
406 self.saw_jfif = true;
407 }
408 }
409 0xEE => {
410 if seg.starts_with(b"Adobe") && seg.len() >= 12 {
411 self.adobe_transform = Some(seg[11]);
412 }
413 }
414 0xDA => {
415 if header_only {
416 return if self.comps.is_empty() {
417 Err(Error::Corrupt("SOS before SOF"))
418 } else {
419 Ok(())
420 };
421 }
422 let end = self.scan(seg, p + len)?;
423 p = end;
424 continue;
425 }
426 _ => {}
427 }
428 p += len;
429 }
430 }
431
432 fn frame(&mut self, seg: &[u8]) -> Result<(), Error> {
433 if seg.len() < 6 {
434 return Err(Error::Corrupt("SOF"));
435 }
436 if seg[0] != 8 {
437 return Err(Error::Unsupported("sample precision other than 8"));
438 }
439 self.height = usize::from(seg[1]) << 8 | usize::from(seg[2]);
440 self.width = usize::from(seg[3]) << 8 | usize::from(seg[4]);
441 let n = usize::from(seg[5]);
442 if self.height == 0 {
443 return Err(Error::Unsupported("DNL-defined height"));
444 }
445 if self.width == 0 || !(n == 1 || n == 3 || n == 4) {
446 return Err(Error::Unsupported("component count"));
447 }
448 if seg.len() < 6 + 3 * n {
449 return Err(Error::Corrupt("SOF components"));
450 }
451 self.comps.clear();
452 for i in 0..n {
453 let c = &seg[6 + 3 * i..9 + 3 * i];
454 let (h, v) = (usize::from(c[1] >> 4), usize::from(c[1] & 15));
455 if !(1..=4).contains(&h) || !(1..=4).contains(&v) || c[2] > 3 {
456 return Err(Error::Corrupt("sampling factors"));
457 }
458 self.comps.push(Component {
459 id: c[0],
460 h,
461 v,
462 tq: usize::from(c[2]),
463 bw: 0,
464 bh: 0,
465 dw: 0,
466 dh: 0,
467 dct: 8,
468 coefs: Vec::new(),
469 samples: Vec::new(),
470 dc_tbl: 0,
471 ac_tbl: 0,
472 dc_pred: 0,
473 });
474 }
475 self.hmax = self.comps.iter().map(|c| c.h).max().unwrap_or(1);
476 self.vmax = self.comps.iter().map(|c| c.v).max().unwrap_or(1);
477 self.mcux = self.width.div_ceil(8 * self.hmax);
478 self.mcuy = self.height.div_ceil(8 * self.vmax);
479 let (w, h, hmax, vmax, mcux, mcuy, progressive, min_dct) = (
480 self.width,
481 self.height,
482 self.hmax,
483 self.vmax,
484 self.mcux,
485 self.mcuy,
486 self.progressive,
487 self.min_dct,
488 );
489 for c in &mut self.comps {
490 c.bw = mcux * c.h;
491 c.bh = mcuy * c.v;
492 let mut ssize = min_dct;
495 while ssize < 8
496 && (hmax * min_dct).is_multiple_of(c.h * ssize * 2)
497 && (vmax * min_dct).is_multiple_of(c.v * ssize * 2)
498 {
499 ssize *= 2;
500 }
501 c.dct = ssize;
502 c.dw = (w * c.h * c.dct).div_ceil(hmax * 8);
503 c.dh = (h * c.v * c.dct).div_ceil(vmax * 8);
504 let blocks = c.bw * c.bh;
505 if blocks > (1usize << 26) {
506 return Err(Error::Unsupported("image too large"));
507 }
508 if progressive {
509 c.coefs = vec![0; blocks * 64];
510 }
511 c.samples = vec![0; blocks * c.dct * c.dct];
512 }
513 Ok(())
514 }
515
516 fn dht(&mut self, mut seg: &[u8]) -> Result<(), Error> {
517 while !seg.is_empty() {
518 if seg.len() < 17 {
519 return Err(Error::Corrupt("DHT"));
520 }
521 let class = seg[0] >> 4;
522 let id = usize::from(seg[0] & 15);
523 if id > 3 || class > 1 {
524 return Err(Error::Corrupt("DHT id"));
525 }
526 let mut bits = [0u8; 16];
527 bits.copy_from_slice(&seg[1..17]);
528 let total: usize = bits.iter().map(|&b| usize::from(b)).sum();
529 if total > 256 || seg.len() < 17 + total {
530 return Err(Error::Corrupt("DHT counts"));
531 }
532 let table = Huffman::build(&bits, seg[17..17 + total].to_vec())?;
533 if class == 0 {
534 self.dc[id] = table;
535 } else {
536 self.ac[id] = table;
537 }
538 seg = &seg[17 + total..];
539 }
540 Ok(())
541 }
542
543 fn dqt(&mut self, mut seg: &[u8]) -> Result<(), Error> {
544 while !seg.is_empty() {
545 let pq = seg[0] >> 4;
546 let tq = usize::from(seg[0] & 15);
547 if tq > 3 || pq > 1 {
548 return Err(Error::Corrupt("DQT id"));
549 }
550 let n = if pq == 0 { 64 } else { 128 };
551 if seg.len() < 1 + n {
552 return Err(Error::Corrupt("DQT"));
553 }
554 for k in 0..64 {
555 let v = if pq == 0 {
556 u16::from(seg[1 + k])
557 } else {
558 u16::from(seg[1 + 2 * k]) << 8 | u16::from(seg[2 + 2 * k])
559 };
560 self.qt[tq][ZIGZAG[k]] = v;
561 }
562 self.qt_present[tq] = true;
563 seg = &seg[1 + n..];
564 }
565 Ok(())
566 }
567
568 fn scan(&mut self, seg: &[u8], start: usize) -> Result<usize, Error> {
571 if self.comps.is_empty() {
572 return Err(Error::Corrupt("SOS before SOF"));
573 }
574 let ns = usize::from(*seg.first().ok_or(Error::Corrupt("SOS"))?);
575 if ns == 0 || ns > 4 || seg.len() < 1 + 2 * ns + 3 {
576 return Err(Error::Corrupt("SOS"));
577 }
578 let mut in_scan = Vec::with_capacity(ns);
579 for i in 0..ns {
580 let cs = seg[1 + 2 * i];
581 let t = seg[2 + 2 * i];
582 let ci = self
583 .comps
584 .iter()
585 .position(|c| c.id == cs)
586 .ok_or(Error::Corrupt("SOS component"))?;
587 self.comps[ci].dc_tbl = usize::from(t >> 4).min(3);
588 self.comps[ci].ac_tbl = usize::from(t & 15).min(3);
589 in_scan.push(ci);
590 }
591 let ss = usize::from(seg[1 + 2 * ns]);
592 let se = usize::from(seg[2 + 2 * ns]);
593 let ah = u32::from(seg[3 + 2 * ns] >> 4);
594 let al = u32::from(seg[3 + 2 * ns] & 15);
595 if self.progressive {
596 if ss > se || se > 63 || (ss == 0 && se != 0) || (ss > 0 && ns != 1) || al > 13 {
597 return Err(Error::Corrupt("progressive scan parameters"));
598 }
599 } else if ss != 0 || se != 63 || ah != 0 || al != 0 {
600 return Err(Error::Corrupt("sequential scan parameters"));
601 }
602 for c in &mut self.comps {
603 c.dc_pred = 0;
604 }
605 self.eobrun = 0;
606 let mut rd = Reader::new(self.data, start);
607
608 let (mcus_x, mcus_y) = if ns == 1 {
615 let c = &self.comps[in_scan[0]];
616 (
617 (self.width * c.h).div_ceil(self.hmax * 8),
618 (self.height * c.v).div_ceil(self.vmax * 8),
619 )
620 } else {
621 (self.mcux, self.mcuy)
622 };
623 let total = mcus_x * mcus_y;
624 let mut count = 0usize;
625 for my in 0..mcus_y {
626 for mx in 0..mcus_x {
627 if self.restart_interval > 0
628 && count > 0
629 && count.is_multiple_of(self.restart_interval)
630 {
631 self.restart(&mut rd);
632 }
633 if ns == 1 {
634 let ci = in_scan[0];
635 self.block(&mut rd, ci, mx, my, ss, se, ah, al)?;
636 } else {
637 for &ci in &in_scan {
638 let (h, v) = (self.comps[ci].h, self.comps[ci].v);
639 for by in 0..v {
640 for bx in 0..h {
641 self.block(&mut rd, ci, mx * h + bx, my * v + by, ss, se, ah, al)?;
642 }
643 }
644 }
645 }
646 count += 1;
647 if count == total {
648 break;
649 }
650 }
651 }
652 Ok(rd.marker_position())
653 }
654
655 fn restart(&mut self, rd: &mut Reader<'_>) {
658 let mut p = rd.marker_position();
659 while p < self.data.len() && self.data[p] == 0xFF {
661 p += 1;
662 }
663 if p < self.data.len() && (0xD0..=0xD7).contains(&self.data[p]) {
664 p += 1;
665 }
666 rd.reset();
667 rd.pos = p;
668 for c in &mut self.comps {
669 c.dc_pred = 0;
670 }
671 self.eobrun = 0;
672 }
673
674 #[allow(clippy::too_many_arguments)]
675 fn block(
676 &mut self,
677 rd: &mut Reader<'_>,
678 ci: usize,
679 bx: usize,
680 by: usize,
681 ss: usize,
682 se: usize,
683 ah: u32,
684 al: u32,
685 ) -> Result<(), Error> {
686 let (bw, bh) = (self.comps[ci].bw, self.comps[ci].bh);
687 if bx >= bw || by >= bh {
688 return Err(Error::Corrupt("block outside the grid"));
689 }
690 let bi = by * bw + bx;
691 if !self.progressive {
692 let mut coef = [0i16; 64];
693 let dc = &self.dc[self.comps[ci].dc_tbl];
694 let ac = &self.ac[self.comps[ci].ac_tbl];
695 let t = rd.decode(dc)?;
696 let diff = rd.receive_extend(u32::from(t));
697 let c = &mut self.comps[ci];
698 c.dc_pred = c.dc_pred.wrapping_add(diff);
699 coef[0] = c.dc_pred as i16;
700 let mut k = 1usize;
701 while k < 64 {
702 let rs = rd.decode(ac)?;
703 let r = usize::from(rs >> 4);
704 let s = u32::from(rs & 15);
705 if s == 0 {
706 if r == 15 {
707 k += 16;
708 continue;
709 }
710 break;
711 }
712 k += r;
713 if k > 63 {
714 break;
715 }
716 coef[ZIGZAG[k]] = rd.receive_extend(s) as i16;
717 k += 1;
718 }
719 let q = &self.qt[self.comps[ci].tq.min(3)];
720 let c = &mut self.comps[ci];
721 idct(c.dct, &coef, q, &mut c.samples, bi, bw);
722 return Ok(());
723 }
724
725 let dc = &self.dc[self.comps[ci].dc_tbl];
727 let ac = &self.ac[self.comps[ci].ac_tbl];
728 let c = &mut self.comps[ci];
729 let coef = &mut c.coefs[bi * 64..bi * 64 + 64];
730 if ss == 0 {
731 if ah == 0 {
732 let t = rd.decode(dc)?;
734 let diff = rd.receive_extend(u32::from(t));
735 c.dc_pred = c.dc_pred.wrapping_add(diff);
736 coef[0] = (c.dc_pred << al) as i16;
737 } else if rd.bit() == 1 {
738 coef[0] |= (1i32 << al) as i16;
740 }
741 return Ok(());
742 }
743 if ah == 0 {
744 if self.eobrun > 0 {
746 self.eobrun -= 1;
747 return Ok(());
748 }
749 let mut k = ss;
750 while k <= se {
751 let rs = rd.decode(ac)?;
752 let r = u32::from(rs >> 4);
753 let s = u32::from(rs & 15);
754 if s != 0 {
755 k += r as usize;
756 if k > 63 {
757 break;
758 }
759 let v = rd.receive_extend(s);
760 coef[ZIGZAG[k]] = (v << al) as i16;
761 } else {
762 if r != 15 {
763 self.eobrun = 1 << r;
764 if r > 0 {
765 self.eobrun += rd.bits(r);
766 }
767 self.eobrun -= 1;
768 break;
769 }
770 k += 15;
771 }
772 k += 1;
773 }
774 return Ok(());
775 }
776 let p1: i16 = (1i32 << al) as i16;
778 let m1: i16 = (-1i32 << al) as i16;
779 let mut k = ss;
780 if self.eobrun == 0 {
781 while k <= se {
782 let rs = rd.decode(ac)?;
783 let mut r = i32::from(rs >> 4);
784 let mut s = i32::from(rs & 15);
785 if s != 0 {
786 s = if rd.bit() == 1 {
788 i32::from(p1)
789 } else {
790 i32::from(m1)
791 };
792 } else if r != 15 {
793 self.eobrun = 1 << r;
794 if r > 0 {
795 self.eobrun += rd.bits(r as u32);
796 }
797 break;
798 }
799 loop {
802 let pos = ZIGZAG[k];
803 if coef[pos] != 0 {
804 if rd.bit() == 1 && (coef[pos] & p1) == 0 {
805 coef[pos] = if coef[pos] >= 0 {
806 coef[pos].wrapping_add(p1)
807 } else {
808 coef[pos].wrapping_add(m1)
809 };
810 }
811 } else {
812 r -= 1;
813 if r < 0 {
814 break;
815 }
816 }
817 k += 1;
818 if k > se {
819 break;
820 }
821 }
822 if s != 0 && k <= se {
823 coef[ZIGZAG[k]] = s as i16;
824 }
825 k += 1;
826 }
827 }
828 if self.eobrun > 0 {
829 while k <= se {
830 let pos = ZIGZAG[k];
831 if coef[pos] != 0 && rd.bit() == 1 && (coef[pos] & p1) == 0 {
832 coef[pos] = if coef[pos] >= 0 {
833 coef[pos].wrapping_add(p1)
834 } else {
835 coef[pos].wrapping_add(m1)
836 };
837 }
838 k += 1;
839 }
840 self.eobrun -= 1;
841 }
842 Ok(())
843 }
844
845 fn finish(mut self, color_transform: bool) -> Result<Image, Error> {
847 if self.comps.is_empty() {
848 return Err(Error::Corrupt("no frame"));
849 }
850 for c in &self.comps {
851 if !self.qt_present[c.tq.min(3)] {
852 return Err(Error::Corrupt("missing quantization table"));
853 }
854 }
855 if self.progressive {
856 for ci in 0..self.comps.len() {
857 let q = self.qt[self.comps[ci].tq.min(3)];
858 let c = &mut self.comps[ci];
859 let bw = c.bw;
860 for bi in 0..c.bw * c.bh {
861 let mut coef = [0i16; 64];
862 coef.copy_from_slice(&c.coefs[bi * 64..bi * 64 + 64]);
863 idct(c.dct, &coef, &q, &mut c.samples, bi, bw);
864 }
865 c.coefs = Vec::new();
866 }
867 }
868 let (w, h) = (
870 (self.width * self.min_dct).div_ceil(8),
871 (self.height * self.min_dct).div_ceil(8),
872 );
873 let planes: Vec<Vec<u8>> = self.comps.iter().map(|c| self.upsample(c, w, h)).collect();
874 let n = self.comps.len();
875 let space = self.color_space();
876 let convert = n == 3
879 && space == ColorSpace::YCbCr
880 && (color_transform || self.adobe_transform.is_some());
881 let mut out = vec![0u8; w * h * n];
882 if n == 1 {
883 out.copy_from_slice(&planes[0][..w * h]);
884 } else if n == 4 {
885 let ycck = self.adobe_transform == Some(2);
888 let t = ycc_tables();
889 for i in 0..w * h {
890 if ycck {
891 let y = i32::from(planes[0][i]);
892 let cb = usize::from(planes[1][i]);
893 let cr = usize::from(planes[2][i]);
894 out[4 * i] = range_limit(255 - (y + t.cr_r[cr]));
895 out[4 * i + 1] = range_limit(255 - (y + ((t.cb_g[cb] + t.cr_g[cr]) >> 16)));
896 out[4 * i + 2] = range_limit(255 - (y + t.cb_b[cb]));
897 } else {
898 out[4 * i] = planes[0][i];
899 out[4 * i + 1] = planes[1][i];
900 out[4 * i + 2] = planes[2][i];
901 }
902 out[4 * i + 3] = planes[3][i];
903 }
904 } else if convert {
905 let t = ycc_tables();
906 for i in 0..w * h {
907 let y = i32::from(planes[0][i]);
908 let cb = usize::from(planes[1][i]);
909 let cr = usize::from(planes[2][i]);
910 out[3 * i] = range_limit(y + t.cr_r[cr]);
911 out[3 * i + 1] = range_limit(y + ((t.cb_g[cb] + t.cr_g[cr]) >> 16));
912 out[3 * i + 2] = range_limit(y + t.cb_b[cb]);
913 }
914 } else {
915 for i in 0..w * h {
916 out[3 * i] = planes[0][i];
917 out[3 * i + 1] = planes[1][i];
918 out[3 * i + 2] = planes[2][i];
919 }
920 }
921 Ok(Image {
922 width: w,
923 height: h,
924 channels: n,
925 data: out,
926 adobe_inverted: n == 4 && self.adobe_transform.is_some(),
927 })
928 }
929
930 fn color_space(&self) -> ColorSpace {
932 if self.comps.len() == 1 {
933 return ColorSpace::Gray;
934 }
935 if self.saw_jfif {
936 return ColorSpace::YCbCr;
937 }
938 if let Some(t) = self.adobe_transform {
939 return if t == 0 {
940 ColorSpace::Rgb
941 } else {
942 ColorSpace::YCbCr
943 };
944 }
945 let ids = [self.comps[0].id, self.comps[1].id, self.comps[2].id];
946 if ids == [b'R', b'G', b'B'] {
947 ColorSpace::Rgb
948 } else {
949 ColorSpace::YCbCr
950 }
951 }
952
953 fn upsample(&self, c: &Component, w: usize, h: usize) -> Vec<u8> {
961 let stride = c.bw * c.dct;
962 let (dw, dh) = (c.dw, c.dh);
963 let row = |r: usize| -> &[u8] {
964 let r = r.min(dh.saturating_sub(1));
965 &c.samples[r * stride..r * stride + dw]
966 };
967 let h_in = c.h * c.dct / self.min_dct;
968 let v_in = c.v * c.dct / self.min_dct;
969 let (h_out, v_out) = (self.hmax, self.vmax);
970 let do_fancy = self.min_dct > 1;
971 let mut out = vec![0u8; w * h];
972 let replicate = |out: &mut Vec<u8>, h_exp: usize, v_exp: usize| {
973 for y in 0..h {
974 let src = row(y / v_exp);
975 for x in 0..w {
976 out[y * w + x] = src[(x / h_exp).min(dw - 1)];
977 }
978 }
979 };
980 if h_in == h_out && v_in == v_out {
981 for y in 0..h {
982 out[y * w..y * w + w].copy_from_slice(&row(y)[..w]);
983 }
984 } else if h_in * 2 == h_out && v_in == v_out {
985 if do_fancy && dw > 2 {
986 let mut line = vec![0u8; dw * 2];
987 for y in 0..h {
988 h2v1_fancy(row(y), &mut line);
989 out[y * w..y * w + w].copy_from_slice(&line[..w]);
990 }
991 } else {
992 replicate(&mut out, 2, 1);
993 }
994 } else if h_in == h_out && v_in * 2 == v_out && do_fancy {
995 for r in 0..dh {
996 for v in 0..2 {
997 let y = 2 * r + v;
998 if y >= h {
999 break;
1000 }
1001 let near = row(r);
1002 let far = if v == 0 {
1003 row(r.saturating_sub(1))
1004 } else {
1005 row(r + 1)
1006 };
1007 let bias: u32 = if v == 0 { 1 } else { 2 };
1008 for x in 0..w {
1009 out[y * w + x] =
1010 ((3 * u32::from(near[x]) + u32::from(far[x]) + bias) >> 2) as u8;
1011 }
1012 }
1013 }
1014 } else if h_in * 2 == h_out && v_in * 2 == v_out {
1015 if do_fancy && dw > 2 {
1016 let mut line = vec![0u8; dw * 2];
1017 for r in 0..dh {
1018 for v in 0..2 {
1019 let y = 2 * r + v;
1020 if y >= h {
1021 break;
1022 }
1023 let near = row(r);
1024 let far = if v == 0 {
1025 row(r.saturating_sub(1))
1026 } else {
1027 row(r + 1)
1028 };
1029 h2v2_fancy(near, far, &mut line);
1030 out[y * w..y * w + w].copy_from_slice(&line[..w]);
1031 }
1032 }
1033 } else {
1034 replicate(&mut out, 2, 2);
1035 }
1036 } else if h_in > 0 && v_in > 0 && h_out.is_multiple_of(h_in) && v_out.is_multiple_of(v_in) {
1037 replicate(&mut out, h_out / h_in, v_out / v_in);
1039 } else {
1040 }
1043 out
1044 }
1045}
1046
1047fn h2v1_fancy(input: &[u8], out: &mut [u8]) {
1050 let n = input.len();
1051 let at = |i: usize| u32::from(input[i]);
1052 out[0] = input[0];
1053 out[1] = ((at(0) * 3 + at(1) + 2) >> 2) as u8;
1054 for i in 1..n - 1 {
1055 let v = at(i) * 3;
1056 out[2 * i] = ((v + at(i - 1) + 1) >> 2) as u8;
1057 out[2 * i + 1] = ((v + at(i + 1) + 2) >> 2) as u8;
1058 }
1059 out[2 * (n - 1)] = ((at(n - 1) * 3 + at(n - 2) + 1) >> 2) as u8;
1060 out[2 * (n - 1) + 1] = input[n - 1];
1061}
1062
1063fn h2v2_fancy(near: &[u8], far: &[u8], out: &mut [u8]) {
1066 let n = near.len();
1067 let colsum = |i: usize| u32::from(near[i]) * 3 + u32::from(far[i]);
1068 let mut this = colsum(0);
1069 let mut next = colsum(1);
1070 out[0] = ((this * 4 + 8) >> 4) as u8;
1071 out[1] = ((this * 3 + next + 7) >> 4) as u8;
1072 let mut last = this;
1073 this = next;
1074 for i in 1..n - 1 {
1075 next = colsum(i + 1);
1076 out[2 * i] = ((this * 3 + last + 8) >> 4) as u8;
1077 out[2 * i + 1] = ((this * 3 + next + 7) >> 4) as u8;
1078 last = this;
1079 this = next;
1080 }
1081 out[2 * (n - 1)] = ((this * 3 + last + 8) >> 4) as u8;
1082 out[2 * (n - 1) + 1] = ((this * 4 + 7) >> 4) as u8;
1083}
1084
1085struct YccTables {
1086 cr_r: [i32; 256],
1087 cb_b: [i32; 256],
1088 cr_g: [i32; 256],
1089 cb_g: [i32; 256],
1090}
1091
1092fn ycc_tables() -> YccTables {
1095 const SCALEBITS: i32 = 16;
1096 const ONE_HALF: i32 = 1 << (SCALEBITS - 1);
1097 let fix = |x: f64| (x * f64::from(1i32 << SCALEBITS) + 0.5) as i32;
1098 let mut t = YccTables {
1099 cr_r: [0; 256],
1100 cb_b: [0; 256],
1101 cr_g: [0; 256],
1102 cb_g: [0; 256],
1103 };
1104 for i in 0..256i32 {
1105 let x = i - 128;
1106 t.cr_r[i as usize] = (fix(1.402) * x + ONE_HALF) >> SCALEBITS;
1107 t.cb_b[i as usize] = (fix(1.772) * x + ONE_HALF) >> SCALEBITS;
1108 t.cr_g[i as usize] = -fix(0.71414) * x;
1109 t.cb_g[i as usize] = -fix(0.34414) * x + ONE_HALF;
1110 }
1111 t
1112}
1113
1114fn range_limit(v: i32) -> u8 {
1116 v.clamp(0, 255) as u8
1117}
1118
1119fn idct_range_limit(x: i32) -> u8 {
1123 let i = (x & 1023) as usize;
1124 match i {
1125 0..=127 => (128 + i) as u8,
1126 128..=511 => 255,
1127 512..=895 => 0,
1128 _ => (i - 896) as u8,
1129 }
1130}
1131
1132fn idct(dct: usize, coef: &[i16; 64], q: &[u16; 64], samples: &mut [u8], bi: usize, bw: usize) {
1137 match dct {
1138 8 => idct_islow(coef, q, samples, bi, bw),
1139 4 => idct_4x4(coef, q, samples, bi, bw),
1140 2 => idct_2x2(coef, q, samples, bi, bw),
1141 _ => idct_1x1(coef, q, samples, bi, bw),
1142 }
1143}
1144
1145const CONST_BITS: i32 = 13;
1146const PASS1_BITS: i32 = 2;
1147
1148#[inline(always)]
1149fn descale(x: i32, n: i32) -> i32 {
1150 x.wrapping_add(1 << (n - 1)) >> n
1151}
1152
1153#[inline(always)]
1154fn mul(a: i32, b: i32) -> i32 {
1155 a.wrapping_mul(b)
1156}
1157
1158fn idct_4x4(coef: &[i16; 64], q: &[u16; 64], samples: &mut [u8], bi: usize, bw: usize) {
1161 const FIX_0_211164243: i32 = 1730;
1162 const FIX_0_509795579: i32 = 4176;
1163 const FIX_0_601344887: i32 = 4926;
1164 const FIX_0_765366865: i32 = 6270;
1165 const FIX_0_899976223: i32 = 7373;
1166 const FIX_1_061594337: i32 = 8697;
1167 const FIX_1_451774981: i32 = 11893;
1168 const FIX_1_847759065: i32 = 15137;
1169 const FIX_2_172734803: i32 = 17799;
1170 const FIX_2_562915447: i32 = 20995;
1171 let dq = |k: usize| i32::from(coef[k]).wrapping_mul(i32::from(q[k]));
1172 let mut ws = [0i32; 32]; for col in 0..8 {
1174 if col == 4 {
1175 continue;
1176 }
1177 if [1, 2, 3, 5, 6, 7].iter().all(|&r| coef[r * 8 + col] == 0) {
1178 let dc = dq(col) << PASS1_BITS;
1179 for r in 0..4 {
1180 ws[r * 8 + col] = dc;
1181 }
1182 continue;
1183 }
1184 let tmp0 = dq(col) << (CONST_BITS + 1);
1185 let z2 = dq(2 * 8 + col);
1186 let z3 = dq(6 * 8 + col);
1187 let tmp2 = mul(z2, FIX_1_847759065).wrapping_add(mul(z3, -FIX_0_765366865));
1188 let tmp10 = tmp0.wrapping_add(tmp2);
1189 let tmp12 = tmp0.wrapping_sub(tmp2);
1190 let z1 = dq(7 * 8 + col);
1191 let z2 = dq(5 * 8 + col);
1192 let z3 = dq(3 * 8 + col);
1193 let z4 = dq(8 + col);
1194 let tmp0 = mul(z1, -FIX_0_211164243)
1195 .wrapping_add(mul(z2, FIX_1_451774981))
1196 .wrapping_add(mul(z3, -FIX_2_172734803))
1197 .wrapping_add(mul(z4, FIX_1_061594337));
1198 let tmp2 = mul(z1, -FIX_0_509795579)
1199 .wrapping_add(mul(z2, -FIX_0_601344887))
1200 .wrapping_add(mul(z3, FIX_0_899976223))
1201 .wrapping_add(mul(z4, FIX_2_562915447));
1202 let n = CONST_BITS - PASS1_BITS + 1;
1203 ws[col] = descale(tmp10.wrapping_add(tmp2), n);
1204 ws[3 * 8 + col] = descale(tmp10.wrapping_sub(tmp2), n);
1205 ws[8 + col] = descale(tmp12.wrapping_add(tmp0), n);
1206 ws[2 * 8 + col] = descale(tmp12.wrapping_sub(tmp0), n);
1207 }
1208 let stride = bw * 4;
1209 let (bx, by) = (bi % bw, bi / bw);
1210 for row in 0..4 {
1211 let w = &ws[row * 8..row * 8 + 8];
1212 let off = (by * 4 + row) * stride + bx * 4;
1213 let out = &mut samples[off..off + 4];
1214 if [1, 2, 3, 5, 6, 7].iter().all(|&k| w[k] == 0) {
1215 out.fill(idct_range_limit(descale(w[0], PASS1_BITS + 3)));
1216 continue;
1217 }
1218 let tmp0 = w[0] << (CONST_BITS + 1);
1219 let tmp2 = mul(w[2], FIX_1_847759065).wrapping_add(mul(w[6], -FIX_0_765366865));
1220 let tmp10 = tmp0.wrapping_add(tmp2);
1221 let tmp12 = tmp0.wrapping_sub(tmp2);
1222 let (z1, z2, z3, z4) = (w[7], w[5], w[3], w[1]);
1223 let tmp0 = mul(z1, -FIX_0_211164243)
1224 .wrapping_add(mul(z2, FIX_1_451774981))
1225 .wrapping_add(mul(z3, -FIX_2_172734803))
1226 .wrapping_add(mul(z4, FIX_1_061594337));
1227 let tmp2 = mul(z1, -FIX_0_509795579)
1228 .wrapping_add(mul(z2, -FIX_0_601344887))
1229 .wrapping_add(mul(z3, FIX_0_899976223))
1230 .wrapping_add(mul(z4, FIX_2_562915447));
1231 let n = CONST_BITS + PASS1_BITS + 3 + 1;
1232 out[0] = idct_range_limit(descale(tmp10.wrapping_add(tmp2), n));
1233 out[3] = idct_range_limit(descale(tmp10.wrapping_sub(tmp2), n));
1234 out[1] = idct_range_limit(descale(tmp12.wrapping_add(tmp0), n));
1235 out[2] = idct_range_limit(descale(tmp12.wrapping_sub(tmp0), n));
1236 }
1237}
1238
1239fn idct_2x2(coef: &[i16; 64], q: &[u16; 64], samples: &mut [u8], bi: usize, bw: usize) {
1241 const FIX_0_720959822: i32 = 5906;
1242 const FIX_0_850430095: i32 = 6967;
1243 const FIX_1_272758580: i32 = 10426;
1244 const FIX_3_624509785: i32 = 29692;
1245 let dq = |k: usize| i32::from(coef[k]).wrapping_mul(i32::from(q[k]));
1246 let mut ws = [0i32; 16]; for col in [0usize, 1, 3, 5, 7] {
1248 if [1, 3, 5, 7].iter().all(|&r| coef[r * 8 + col] == 0) {
1249 let dc = dq(col) << PASS1_BITS;
1250 ws[col] = dc;
1251 ws[8 + col] = dc;
1252 continue;
1253 }
1254 let tmp10 = dq(col) << (CONST_BITS + 2);
1255 let tmp0 = mul(dq(7 * 8 + col), -FIX_0_720959822)
1256 .wrapping_add(mul(dq(5 * 8 + col), FIX_0_850430095))
1257 .wrapping_add(mul(dq(3 * 8 + col), -FIX_1_272758580))
1258 .wrapping_add(mul(dq(8 + col), FIX_3_624509785));
1259 let n = CONST_BITS - PASS1_BITS + 2;
1260 ws[col] = descale(tmp10.wrapping_add(tmp0), n);
1261 ws[8 + col] = descale(tmp10.wrapping_sub(tmp0), n);
1262 }
1263 let stride = bw * 2;
1264 let (bx, by) = (bi % bw, bi / bw);
1265 for row in 0..2 {
1266 let w = &ws[row * 8..row * 8 + 8];
1267 let off = (by * 2 + row) * stride + bx * 2;
1268 let out = &mut samples[off..off + 2];
1269 if [1, 3, 5, 7].iter().all(|&k| w[k] == 0) {
1270 out.fill(idct_range_limit(descale(w[0], PASS1_BITS + 3)));
1271 continue;
1272 }
1273 let tmp10 = w[0] << (CONST_BITS + 2);
1274 let tmp0 = mul(w[7], -FIX_0_720959822)
1275 .wrapping_add(mul(w[5], FIX_0_850430095))
1276 .wrapping_add(mul(w[3], -FIX_1_272758580))
1277 .wrapping_add(mul(w[1], FIX_3_624509785));
1278 let n = CONST_BITS + PASS1_BITS + 3 + 2;
1279 out[0] = idct_range_limit(descale(tmp10.wrapping_add(tmp0), n));
1280 out[1] = idct_range_limit(descale(tmp10.wrapping_sub(tmp0), n));
1281 }
1282}
1283
1284fn idct_1x1(coef: &[i16; 64], q: &[u16; 64], samples: &mut [u8], bi: usize, bw: usize) {
1286 let dc = descale(i32::from(coef[0]).wrapping_mul(i32::from(q[0])), 3);
1287 let (bx, by) = (bi % bw, bi / bw);
1288 samples[by * bw + bx] = idct_range_limit(dc);
1289}
1290
1291fn idct_islow(coef: &[i16; 64], q: &[u16; 64], samples: &mut [u8], bi: usize, bw: usize) {
1295 const FIX_0_298631336: i32 = 2446;
1296 const FIX_0_390180644: i32 = 3196;
1297 const FIX_0_541196100: i32 = 4433;
1298 const FIX_0_765366865: i32 = 6270;
1299 const FIX_0_899976223: i32 = 7373;
1300 const FIX_1_175875602: i32 = 9633;
1301 const FIX_1_501321110: i32 = 12299;
1302 const FIX_1_847759065: i32 = 15137;
1303 const FIX_1_961570560: i32 = 16069;
1304 const FIX_2_053119869: i32 = 16819;
1305 const FIX_2_562915447: i32 = 20995;
1306 const FIX_3_072711026: i32 = 25172;
1307
1308 let dq = |k: usize| i32::from(coef[k]).wrapping_mul(i32::from(q[k]));
1309 let mut ws = [0i32; 64];
1310
1311 for col in 0..8 {
1313 if (1..8).all(|r| coef[r * 8 + col] == 0) {
1314 let dc = dq(col) << PASS1_BITS;
1315 for r in 0..8 {
1316 ws[r * 8 + col] = dc;
1317 }
1318 continue;
1319 }
1320 let z2 = dq(2 * 8 + col);
1321 let z3 = dq(6 * 8 + col);
1322 let z1 = mul(z2.wrapping_add(z3), FIX_0_541196100);
1323 let tmp2 = z1.wrapping_add(mul(z3, -FIX_1_847759065));
1324 let tmp3 = z1.wrapping_add(mul(z2, FIX_0_765366865));
1325 let z2 = dq(col);
1326 let z3 = dq(4 * 8 + col);
1327 let tmp0 = z2.wrapping_add(z3) << CONST_BITS;
1328 let tmp1 = z2.wrapping_sub(z3) << CONST_BITS;
1329 let tmp10 = tmp0.wrapping_add(tmp3);
1330 let tmp13 = tmp0.wrapping_sub(tmp3);
1331 let tmp11 = tmp1.wrapping_add(tmp2);
1332 let tmp12 = tmp1.wrapping_sub(tmp2);
1333
1334 let mut tmp0 = dq(7 * 8 + col);
1335 let mut tmp1 = dq(5 * 8 + col);
1336 let mut tmp2 = dq(3 * 8 + col);
1337 let mut tmp3 = dq(8 + col);
1338 let z1 = tmp0.wrapping_add(tmp3);
1339 let z2 = tmp1.wrapping_add(tmp2);
1340 let z3 = tmp0.wrapping_add(tmp2);
1341 let z4 = tmp1.wrapping_add(tmp3);
1342 let z5 = mul(z3.wrapping_add(z4), FIX_1_175875602);
1343 tmp0 = mul(tmp0, FIX_0_298631336);
1344 tmp1 = mul(tmp1, FIX_2_053119869);
1345 tmp2 = mul(tmp2, FIX_3_072711026);
1346 tmp3 = mul(tmp3, FIX_1_501321110);
1347 let z1 = mul(z1, -FIX_0_899976223);
1348 let z2 = mul(z2, -FIX_2_562915447);
1349 let z3 = mul(z3, -FIX_1_961570560).wrapping_add(z5);
1350 let z4 = mul(z4, -FIX_0_390180644).wrapping_add(z5);
1351 tmp0 = tmp0.wrapping_add(z1).wrapping_add(z3);
1352 tmp1 = tmp1.wrapping_add(z2).wrapping_add(z4);
1353 tmp2 = tmp2.wrapping_add(z2).wrapping_add(z3);
1354 tmp3 = tmp3.wrapping_add(z1).wrapping_add(z4);
1355
1356 let n = CONST_BITS - PASS1_BITS;
1357 ws[col] = descale(tmp10.wrapping_add(tmp3), n);
1358 ws[7 * 8 + col] = descale(tmp10.wrapping_sub(tmp3), n);
1359 ws[8 + col] = descale(tmp11.wrapping_add(tmp2), n);
1360 ws[6 * 8 + col] = descale(tmp11.wrapping_sub(tmp2), n);
1361 ws[2 * 8 + col] = descale(tmp12.wrapping_add(tmp1), n);
1362 ws[5 * 8 + col] = descale(tmp12.wrapping_sub(tmp1), n);
1363 ws[3 * 8 + col] = descale(tmp13.wrapping_add(tmp0), n);
1364 ws[4 * 8 + col] = descale(tmp13.wrapping_sub(tmp0), n);
1365 }
1366
1367 let stride = bw * 8;
1369 let (bx, by) = (bi % bw, bi / bw);
1370 for row in 0..8 {
1371 let w = &ws[row * 8..row * 8 + 8];
1372 let out_off = (by * 8 + row) * stride + bx * 8;
1373 let out = &mut samples[out_off..out_off + 8];
1374 let n = CONST_BITS + PASS1_BITS + 3;
1375 if w[1..].iter().all(|&v| v == 0) {
1376 let dc = idct_range_limit(descale(w[0], PASS1_BITS + 3));
1377 out.fill(dc);
1378 continue;
1379 }
1380 let z2 = w[2];
1381 let z3 = w[6];
1382 let z1 = mul(z2.wrapping_add(z3), FIX_0_541196100);
1383 let tmp2 = z1.wrapping_add(mul(z3, -FIX_1_847759065));
1384 let tmp3 = z1.wrapping_add(mul(z2, FIX_0_765366865));
1385 let tmp0 = w[0].wrapping_add(w[4]) << CONST_BITS;
1386 let tmp1 = w[0].wrapping_sub(w[4]) << CONST_BITS;
1387 let tmp10 = tmp0.wrapping_add(tmp3);
1388 let tmp13 = tmp0.wrapping_sub(tmp3);
1389 let tmp11 = tmp1.wrapping_add(tmp2);
1390 let tmp12 = tmp1.wrapping_sub(tmp2);
1391
1392 let mut tmp0 = w[7];
1393 let mut tmp1 = w[5];
1394 let mut tmp2 = w[3];
1395 let mut tmp3 = w[1];
1396 let z1 = tmp0.wrapping_add(tmp3);
1397 let z2 = tmp1.wrapping_add(tmp2);
1398 let z3 = tmp0.wrapping_add(tmp2);
1399 let z4 = tmp1.wrapping_add(tmp3);
1400 let z5 = mul(z3.wrapping_add(z4), FIX_1_175875602);
1401 tmp0 = mul(tmp0, FIX_0_298631336);
1402 tmp1 = mul(tmp1, FIX_2_053119869);
1403 tmp2 = mul(tmp2, FIX_3_072711026);
1404 tmp3 = mul(tmp3, FIX_1_501321110);
1405 let z1 = mul(z1, -FIX_0_899976223);
1406 let z2 = mul(z2, -FIX_2_562915447);
1407 let z3 = mul(z3, -FIX_1_961570560).wrapping_add(z5);
1408 let z4 = mul(z4, -FIX_0_390180644).wrapping_add(z5);
1409 tmp0 = tmp0.wrapping_add(z1).wrapping_add(z3);
1410 tmp1 = tmp1.wrapping_add(z2).wrapping_add(z4);
1411 tmp2 = tmp2.wrapping_add(z2).wrapping_add(z3);
1412 tmp3 = tmp3.wrapping_add(z1).wrapping_add(z4);
1413
1414 out[0] = idct_range_limit(descale(tmp10.wrapping_add(tmp3), n));
1415 out[7] = idct_range_limit(descale(tmp10.wrapping_sub(tmp3), n));
1416 out[1] = idct_range_limit(descale(tmp11.wrapping_add(tmp2), n));
1417 out[6] = idct_range_limit(descale(tmp11.wrapping_sub(tmp2), n));
1418 out[2] = idct_range_limit(descale(tmp12.wrapping_add(tmp1), n));
1419 out[5] = idct_range_limit(descale(tmp12.wrapping_sub(tmp1), n));
1420 out[3] = idct_range_limit(descale(tmp13.wrapping_add(tmp0), n));
1421 out[4] = idct_range_limit(descale(tmp13.wrapping_sub(tmp0), n));
1422 }
1423}
1424
1425#[cfg(test)]
1426mod tests {
1427 use super::*;
1428
1429 fn fixtures() -> Vec<(String, Vec<u8>, image::DynamicImage)> {
1430 let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/data/jpeg");
1431 let mut out = Vec::new();
1432 let mut names: Vec<_> = std::fs::read_dir(&dir)
1433 .unwrap()
1434 .filter_map(|e| e.ok().map(|e| e.path()))
1435 .filter(|p| p.extension().is_some_and(|e| e == "jpg"))
1436 .collect();
1437 names.sort();
1438 for jpg in names {
1439 let png = jpg.with_extension("png");
1440 let reference = image::open(&png).unwrap_or_else(|e| panic!("{}: {e}", png.display()));
1441 out.push((
1442 jpg.file_stem().unwrap().to_string_lossy().into_owned(),
1443 std::fs::read(&jpg).unwrap(),
1444 reference,
1445 ));
1446 }
1447 assert!(out.len() >= 8, "fixtures missing");
1448 out
1449 }
1450
1451 #[test]
1456 fn matches_libjpeg_on_the_fixtures() {
1457 let mut failures = Vec::new();
1458 for (name, jpg, reference) in fixtures() {
1459 let img = decode(&jpg, true, 1).unwrap_or_else(|e| panic!("{name}: {e:?}"));
1460 let want: Vec<u8> = if img.channels == 1 {
1461 reference.to_luma8().into_raw()
1462 } else {
1463 reference.to_rgb8().into_raw()
1464 };
1465 assert_eq!(
1466 (img.width, img.height),
1467 (reference.width() as usize, reference.height() as usize),
1468 "{name}: size"
1469 );
1470 if img.data != want {
1471 let diff = img.data.iter().zip(&want).filter(|(a, b)| a != b).count();
1472 let max = img
1473 .data
1474 .iter()
1475 .zip(&want)
1476 .map(|(a, b)| (i32::from(*a) - i32::from(*b)).abs())
1477 .max()
1478 .unwrap_or(0);
1479 failures.push(format!(
1480 "{name}: {diff} of {} bytes differ, max |Δ| {max}",
1481 want.len()
1482 ));
1483 }
1484 }
1485 assert!(failures.is_empty(), "{}", failures.join("\n"));
1486 }
1487
1488 #[test]
1489 fn info_reads_the_frame_header() {
1490 let (_, jpg, reference) = fixtures()
1491 .into_iter()
1492 .find(|(n, _, _)| n == "rgb_420")
1493 .unwrap();
1494 let i = info(&jpg).unwrap();
1495 assert_eq!(
1496 (i.width, i.height, i.components),
1497 (reference.width() as usize, reference.height() as usize, 3)
1498 );
1499 }
1500
1501 #[test]
1509 fn reduced_scales_have_the_right_size_and_content() {
1510 for (name, jpg, _) in fixtures() {
1511 let full = decode(&jpg, true, 1).unwrap();
1512 for denom in [2usize, 4, 8] {
1513 let img = decode(&jpg, true, denom as u32).unwrap();
1514 assert_eq!(img.width, full.width.div_ceil(denom), "{name} 1/{denom}");
1515 assert_eq!(img.height, full.height.div_ceil(denom), "{name} 1/{denom}");
1516 assert_eq!(img.channels, full.channels, "{name} 1/{denom}");
1517 let ch = img.channels;
1518 let mut worst = 0i32;
1519 for oy in 0..img.height {
1520 for ox in 0..img.width {
1521 for c in 0..ch {
1522 let (mut sum, mut n) = (0u32, 0u32);
1523 for y in oy * denom..((oy + 1) * denom).min(full.height) {
1524 for x in ox * denom..((ox + 1) * denom).min(full.width) {
1525 sum += u32::from(full.data[(y * full.width + x) * ch + c]);
1526 n += 1;
1527 }
1528 }
1529 let mean = (sum / n.max(1)) as i32;
1530 let got = i32::from(img.data[(oy * img.width + ox) * ch + c]);
1531 worst = worst.max((got - mean).abs());
1532 }
1533 }
1534 }
1535 assert!(
1539 worst < 96,
1540 "{name} 1/{denom}: worst |Δ| {worst} vs the block mean"
1541 );
1542 }
1543 }
1544 }
1545
1546 #[test]
1547 fn a_dc_only_block_is_flat() {
1548 let mut coef = [0i16; 64];
1549 coef[0] = 8; let q = [1u16; 64];
1551 let mut samples = vec![0u8; 64];
1552 idct_islow(&coef, &q, &mut samples, 0, 1);
1553 assert!(samples.iter().all(|&v| v == 129), "{samples:?}");
1554 }
1555
1556 #[test]
1557 fn colour_tables_match_libjpeg_constants() {
1558 let t = ycc_tables();
1559 assert_eq!(t.cr_r[255], 178);
1561 assert_eq!(t.cb_b[0], -227);
1562 assert_eq!(range_limit(300), 255);
1563 assert_eq!(idct_range_limit(-5), 123);
1564 assert_eq!(idct_range_limit(200), 255);
1565 assert_eq!(idct_range_limit(-300), 0);
1566 }
1567}