1use crate::entropy::Reader;
19use crate::format::{
20 AdobeTransform, Frame, IccChunks, Scan, ZIGZAG, adobe_transform, exif_orientation, marker,
21};
22use crate::huffman::HuffmanTable;
23use crate::idct::{self, Scale};
24use otf_pixels_core::{
25 Codec, DecodeCapability, Decoder, Format, ImageDescriptor, Limits, Orientation, PixelFormat,
26 PixelsError, Result, Source,
27};
28
29const MAX_BLOCKS_PER_MCU: u32 = 10;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38enum Colour {
39 Grayscale,
41 YCbCr,
43 Rgb,
45}
46
47#[derive(Debug)]
49struct Plane {
50 stride: usize,
52 samples: Vec<u8>,
54 columns: Vec<u32>,
57 rows: Vec<u32>,
59}
60
61enum Parsed<S: Source> {
63 Baseline(Box<Baseline<S>>),
65 #[cfg(feature = "progressive")]
68 Progressive {
69 replay: Vec<u8>,
71 source: S,
73 orientation: Option<Orientation>,
76 icc: Option<Vec<u8>>,
78 },
79}
80
81#[derive(Debug)]
88pub struct JpegDecoder<S: Source> {
89 inner: Inner<S>,
90 icc: Option<Vec<u8>>,
92}
93
94#[derive(Debug)]
96enum Inner<S: Source> {
97 Baseline(Box<Baseline<S>>),
98 #[cfg(feature = "progressive")]
99 Progressive(crate::progressive::Progressive),
100}
101
102impl<S: Source> JpegDecoder<S> {
103 pub fn new(source: S, limits: Limits) -> Result<Self> {
117 Self::with_scale(source, limits, Scale::Full)
118 }
119
120 pub fn with_scale(source: S, limits: Limits, scale: Scale) -> Result<Self> {
139 let (inner, icc) = match Baseline::with_scale(source, limits, scale)? {
140 Parsed::Baseline(mut baseline) => {
141 let icc = baseline.icc.take();
142 (Inner::Baseline(baseline), icc)
143 }
144 #[cfg(feature = "progressive")]
145 Parsed::Progressive {
146 replay,
147 source,
148 orientation,
149 icc,
150 } => (
151 Inner::Progressive(crate::progressive::Progressive::new(
152 replay,
153 source,
154 limits,
155 orientation,
156 )?),
157 icc,
158 ),
159 };
160 Ok(Self { inner, icc })
161 }
162
163 #[must_use]
167 pub const fn scale(&self) -> Scale {
168 match &self.inner {
169 Inner::Baseline(baseline) => baseline.scale,
170 #[cfg(feature = "progressive")]
171 Inner::Progressive(_) => Scale::Full,
172 }
173 }
174
175 #[must_use]
178 pub const fn is_progressive(&self) -> bool {
179 match &self.inner {
180 Inner::Baseline(_) => false,
181 #[cfg(feature = "progressive")]
182 Inner::Progressive(_) => true,
183 }
184 }
185}
186
187impl<S: Source + std::fmt::Debug> Decoder for JpegDecoder<S> {
188 fn descriptor(&self) -> ImageDescriptor {
189 match &self.inner {
190 Inner::Baseline(baseline) => baseline.descriptor(),
191 #[cfg(feature = "progressive")]
192 Inner::Progressive(progressive) => progressive.descriptor(),
193 }
194 }
195
196 fn icc_profile(&self) -> Option<&[u8]> {
198 self.icc.as_deref()
199 }
200
201 fn orientation(&self) -> Orientation {
203 match &self.inner {
204 Inner::Baseline(baseline) => baseline.orientation,
205 #[cfg(feature = "progressive")]
206 Inner::Progressive(progressive) => progressive.orientation(),
207 }
208 .unwrap_or_default()
209 }
210
211 fn capability(&self) -> DecodeCapability {
212 match &self.inner {
213 Inner::Baseline(baseline) => baseline.capability(),
214 #[cfg(feature = "progressive")]
215 Inner::Progressive(progressive) => progressive.capability(),
216 }
217 }
218
219 fn reduced_descriptor(&self, target: (u32, u32)) -> Option<ImageDescriptor> {
220 match &self.inner {
221 Inner::Baseline(baseline) => baseline.reduced_descriptor(target),
222 #[cfg(feature = "progressive")]
225 Inner::Progressive(_) => None,
226 }
227 }
228
229 fn reduce_to(&mut self, descriptor: ImageDescriptor) -> Result<()> {
230 match &mut self.inner {
231 Inner::Baseline(baseline) => baseline.reduce_to(descriptor),
232 #[cfg(feature = "progressive")]
233 Inner::Progressive(_) => Err(PixelsError::unsupported(
234 "jpeg: a progressive frame decodes at one resolution",
235 )),
236 }
237 }
238
239 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
240 match &mut self.inner {
241 Inner::Baseline(baseline) => baseline.read_row(out),
242 #[cfg(feature = "progressive")]
243 Inner::Progressive(progressive) => progressive.read_row(out),
244 }
245 }
246}
247
248struct Baseline<S: Source> {
250 reader: Reader<S>,
251 descriptor: ImageDescriptor,
252 frame: Frame,
253 scan: Scan,
254 colour: Colour,
255 quant: [[u16; 64]; 4],
257 dc_tables: [Option<HuffmanTable>; 4],
259 ac_tables: [Option<HuffmanTable>; 4],
260 restart_interval: u16,
262 restarts_left: u32,
264 orientation: Option<Orientation>,
266 icc: Option<Vec<u8>>,
268 planes: Vec<Plane>,
269 predictors: Vec<i32>,
271 band: Vec<u8>,
273 band_row: u32,
275 band_height: u32,
277 mcus_per_line: u32,
278 mcu_rows: u32,
279 mcu_row: u32,
281 scale: Scale,
283 limits: Limits,
285 row: u32,
287}
288
289impl<S: Source> std::fmt::Debug for Baseline<S> {
290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291 f.debug_struct("Baseline")
292 .field("descriptor", &self.descriptor)
293 .field("colour", &self.colour)
294 .field("components", &self.frame.components)
295 .field("scale", &self.scale)
296 .field("restart_interval", &self.restart_interval)
297 .field("orientation", &self.orientation)
298 .field("row", &self.row)
299 .finish_non_exhaustive()
300 }
301}
302
303impl<S: Source> Baseline<S> {
304 fn with_scale(source: S, limits: Limits, scale: Scale) -> Result<Parsed<S>> {
331 let mut reader = Reader::new(source);
332 #[cfg(feature = "progressive")]
335 reader.record();
336 let mut quant = [[0_u16; 64]; 4];
337 let mut dc_tables: [Option<HuffmanTable>; 4] = [None, None, None, None];
338 let mut ac_tables: [Option<HuffmanTable>; 4] = [None, None, None, None];
339 let mut restart_interval = 0_u16;
340 let mut orientation = None;
341 let mut icc = IccChunks::default();
342 let mut adobe = None;
343 let mut frame: Option<Frame> = None;
344
345 let first = reader.next_marker()?;
346 if first != marker::SOI {
347 return Err(PixelsError::malformed(
348 "jpeg",
349 format!("stream begins with marker {first:#04x}, not SOI"),
350 ));
351 }
352
353 let scan = loop {
354 let code = reader.next_marker()?;
355 match code {
356 marker::SOF0 | marker::SOF1 => {
357 if frame.is_some() {
358 return Err(PixelsError::malformed(
359 "jpeg",
360 "stream declares more than one frame",
361 ));
362 }
363 #[cfg(feature = "progressive")]
364 reader.forget();
365 let parsed = Frame::parse(&reader.read_segment()?)?;
366 limits.check(u32::from(parsed.width), u32::from(parsed.height))?;
370 frame = Some(parsed);
371 }
372 marker::SOF2 => {
373 #[cfg(feature = "progressive")]
380 {
381 let (replay, source) = reader.into_replay();
382 return Ok(Parsed::Progressive {
383 replay,
384 source,
385 orientation,
386 icc: icc.assemble(),
387 });
388 }
389 #[cfg(not(feature = "progressive"))]
390 return Err(PixelsError::unsupported(
391 "jpeg: progressive JPEG; enable the `progressive` feature of \
392 otf-pixels-codec-jpeg to decode it",
393 ));
394 }
395 marker::DAC => {
396 return Err(PixelsError::unsupported(
397 "jpeg: arithmetic coding; baseline JPEG is Huffman coded",
398 ));
399 }
400 code if marker::is_frame(code) => {
401 return Err(PixelsError::unsupported(format!(
402 "jpeg: frame type {code:#04x} is not baseline"
403 )));
404 }
405 marker::DQT => read_quantization_tables(&reader.read_segment()?, &mut quant)?,
406 marker::DHT => {
407 read_huffman_tables(&reader.read_segment()?, &mut dc_tables, &mut ac_tables)?;
408 }
409 marker::DRI => {
410 let payload = reader.read_segment()?;
411 let (Some(&hi), Some(&lo)) = (payload.first(), payload.get(1)) else {
412 return Err(PixelsError::malformed(
413 "jpeg",
414 "DRI segment carries no interval",
415 ));
416 };
417 restart_interval = u16::from_be_bytes([hi, lo]);
418 }
419 marker::APP1 => {
420 let payload = reader.read_segment()?;
421 orientation = orientation.or_else(|| exif_orientation(&payload));
423 }
424 marker::APP2 => icc.push(&reader.read_segment()?),
425 marker::APP14 => {
426 let payload = reader.read_segment()?;
427 adobe = adobe_transform(&payload).or(adobe);
428 }
429 marker::SOS => {
430 let Some(ref frame) = frame else {
431 return Err(PixelsError::malformed(
432 "jpeg",
433 "scan begins before any frame header",
434 ));
435 };
436 break Scan::parse(&reader.read_segment()?, frame)?;
437 }
438 marker::EOI => {
439 return Err(PixelsError::malformed(
440 "jpeg",
441 "stream ends before any scan",
442 ));
443 }
444 marker::APP0..=marker::APP15 | marker::COM => reader.skip_segment()?,
449 code if marker::is_standalone(code) => {}
450 _ => reader.skip_segment()?,
451 }
452 };
453
454 let Some(frame) = frame else {
455 return Err(PixelsError::malformed("jpeg", "stream has no frame header"));
456 };
457 check_baseline_scan(&scan)?;
458
459 let colour = colour_model(&frame, adobe)?;
460 let pixel = match colour {
461 Colour::Grayscale => PixelFormat::Gray8,
462 Colour::YCbCr | Colour::Rgb => PixelFormat::Rgb8,
463 };
464 let (full_width, full_height) = (u32::from(frame.width), u32::from(frame.height));
468 let descriptor = ImageDescriptor::with_limits(
469 scale.apply(full_width),
470 scale.apply(full_height),
471 pixel,
472 &limits,
473 )?;
474
475 if scan.components.len() != frame.components.len() {
476 return Err(PixelsError::unsupported(
481 "jpeg: non-interleaved baseline scan",
482 ));
483 }
484 for component in &scan.components {
485 let slot = frame
486 .components
487 .get(component.index)
488 .map_or(0, |c| c.quant as usize);
489 if quant
490 .get(slot)
491 .is_none_or(|table| table.iter().all(|&q| q == 0))
492 {
493 return Err(PixelsError::malformed(
494 "jpeg",
495 format!("scan uses quantization table {slot}, which was never defined"),
496 ));
497 }
498 if dc_tables
499 .get(component.dc as usize)
500 .is_none_or(Option::is_none)
501 || ac_tables
502 .get(component.ac as usize)
503 .is_none_or(Option::is_none)
504 {
505 return Err(PixelsError::malformed(
506 "jpeg",
507 format!(
508 "scan uses Huffman tables {}/{}, which were never defined",
509 component.dc, component.ac
510 ),
511 ));
512 }
513 }
514
515 let (h_max, v_max) = (u32::from(frame.h_max()), u32::from(frame.v_max()));
516 let blocks: u32 = frame
517 .components
518 .iter()
519 .map(|c| u32::from(c.h) * u32::from(c.v))
520 .sum();
521 if blocks > MAX_BLOCKS_PER_MCU {
522 return Err(PixelsError::malformed(
523 "jpeg",
524 format!(
525 "an MCU would hold {blocks} blocks; the format allows {MAX_BLOCKS_PER_MCU}"
526 ),
527 ));
528 }
529
530 let mcus_per_line = full_width.div_ceil(h_max * 8);
531 let mcu_rows = full_height.div_ceil(v_max * 8);
532
533 let mut decoder = Self {
537 reader,
538 descriptor,
539 predictors: vec![0; frame.components.len()],
540 frame,
541 scan,
542 colour,
543 quant,
544 dc_tables,
545 ac_tables,
546 restart_interval,
547 restarts_left: u32::from(restart_interval),
548 orientation,
549 icc: icc.assemble(),
550 limits,
551 planes: Vec::new(),
552 band: Vec::new(),
553 band_row: 0,
554 band_height: 0,
555 mcus_per_line,
556 mcu_rows,
557 mcu_row: 0,
558 scale: Scale::Full,
559 row: 0,
560 };
561 decoder.apply_scale(scale)?;
562 Ok(Parsed::Baseline(Box::new(decoder)))
563 }
564
565 fn apply_scale(&mut self, scale: Scale) -> Result<()> {
577 if self.row > 0 || self.mcu_row > 0 {
578 return Err(PixelsError::invalid_argument(
579 "scale",
580 format!("{} rows have already been decoded", self.row),
581 ));
582 }
583 let full_width = u32::from(self.frame.width);
584 let full_height = u32::from(self.frame.height);
585 let (h_max, v_max) = (u32::from(self.frame.h_max()), u32::from(self.frame.v_max()));
586 let descriptor = ImageDescriptor::with_limits(
587 scale.apply(full_width),
588 scale.apply(full_height),
589 self.descriptor.pixel,
590 &self.limits,
591 )?;
592
593 let sample = scale.block_size();
596 let band_height = v_max * sample;
597
598 let mut planes = Vec::with_capacity(self.frame.components.len());
599 for component in &self.frame.components {
600 let (h, v) = (u32::from(component.h), u32::from(component.v));
601 let stride = usize::try_from(self.mcus_per_line * h * sample)
602 .map_err(|_| PixelsError::malformed("jpeg", "component plane overflows"))?;
603 let height = usize::try_from(v * sample).unwrap_or(32);
604 let samples = stride
605 .checked_mul(height)
606 .ok_or_else(|| PixelsError::malformed("jpeg", "component plane overflows"))?;
607 planes.push(Plane {
608 stride,
609 samples: vec![0_u8; samples],
610 columns: (0..descriptor.width).map(|x| x * h / h_max).collect(),
616 rows: (0..band_height).map(|y| y * v / v_max).collect(),
617 });
618 }
619
620 let band = descriptor
621 .row_bytes()
622 .checked_mul(band_height as usize)
623 .ok_or_else(|| PixelsError::malformed("jpeg", "MCU row band overflows"))?;
624
625 self.descriptor = descriptor;
626 self.planes = planes;
627 self.band = vec![0_u8; band];
628 self.band_row = band_height;
631 self.band_height = band_height;
632 self.scale = scale;
633 Ok(())
634 }
635
636 fn fill_band(&mut self) -> Result<()> {
639 if self.mcu_row >= self.mcu_rows {
640 return Err(PixelsError::malformed(
641 "jpeg",
642 "more rows were requested than the frame declares",
643 ));
644 }
645
646 let mut coefficients = [0_i32; 64];
647 let scale = self.scale;
648 let sample = scale.block_size() as usize;
649 for mcu in 0..self.mcus_per_line {
650 if self.restart_interval > 0 && self.restarts_left == 0 {
651 if !self.reader.restart()? {
652 let met = self.reader.pending_marker();
653 return Err(PixelsError::malformed(
654 "jpeg",
655 match met {
656 Some(code) => format!(
657 "expected a restart marker between MCU intervals, met {code:#04x}"
658 ),
659 None => "expected a restart marker between MCU intervals".to_owned(),
660 },
661 ));
662 }
663 self.predictors.iter_mut().for_each(|p| *p = 0);
667 self.restarts_left = u32::from(self.restart_interval);
668 }
669
670 for scanned in &self.scan.components {
671 let Some(component) = self.frame.components.get(scanned.index) else {
672 continue;
673 };
674 let (h, v) = (u32::from(component.h), u32::from(component.v));
675 let (Some(dc), Some(ac)) = (
676 self.dc_tables
677 .get(scanned.dc as usize)
678 .and_then(Option::as_ref),
679 self.ac_tables
680 .get(scanned.ac as usize)
681 .and_then(Option::as_ref),
682 ) else {
683 return Err(PixelsError::malformed(
684 "jpeg",
685 "scan names a Huffman table that was never defined",
686 ));
687 };
688 let quant = self
689 .quant
690 .get(component.quant as usize)
691 .ok_or_else(|| PixelsError::malformed("jpeg", "component names no table"))?;
692 let Some(predictor) = self.predictors.get_mut(scanned.index) else {
693 continue;
694 };
695 let Some(plane) = self.planes.get_mut(scanned.index) else {
696 continue;
697 };
698
699 for block_y in 0..v {
700 for block_x in 0..h {
701 decode_block(
702 &mut self.reader,
703 dc,
704 ac,
705 quant,
706 predictor,
707 &mut coefficients,
708 )?;
709 let x = ((mcu * h) + block_x) as usize * sample;
710 let y = block_y as usize * sample;
711 idct::scaled_block(
712 &coefficients,
713 scale,
714 &mut plane.samples,
715 y * plane.stride + x,
716 plane.stride,
717 );
718 }
719 }
720 }
721
722 if self.restart_interval > 0 {
723 self.restarts_left = self.restarts_left.saturating_sub(1);
724 }
725 }
726
727 self.mcu_row += 1;
728 self.convert_band();
729 self.band_row = 0;
730 Ok(())
731 }
732
733 fn convert_band(&mut self) {
736 let width = self.descriptor.width as usize;
737 let row_bytes = self.descriptor.row_bytes();
738
739 for y in 0..self.band_height as usize {
740 let Some(out) = self
741 .band
742 .get_mut(y * row_bytes..)
743 .and_then(|rest| rest.get_mut(..row_bytes))
744 else {
745 continue;
746 };
747
748 match self.colour {
749 Colour::Grayscale => {
750 let Some(plane) = self.planes.first() else {
751 continue;
752 };
753 let source = plane.row(y);
754 for (x, slot) in out.iter_mut().enumerate().take(width) {
755 *slot = plane.sample(source, x);
756 }
757 }
758 Colour::YCbCr | Colour::Rgb => {
759 let (Some(first), Some(second), Some(third)) =
760 (self.planes.first(), self.planes.get(1), self.planes.get(2))
761 else {
762 continue;
763 };
764 let (a, b, c) = (first.row(y), second.row(y), third.row(y));
765 for (x, pixel) in out.chunks_exact_mut(3).enumerate().take(width) {
766 let samples = [first.sample(a, x), second.sample(b, x), third.sample(c, x)];
767 let rgb = if self.colour == Colour::Rgb {
768 samples
769 } else {
770 ycbcr_to_rgb(samples)
771 };
772 for (slot, value) in pixel.iter_mut().zip(rgb) {
773 *slot = value;
774 }
775 }
776 }
777 }
778 }
779 }
780}
781
782impl Plane {
783 fn row(&self, y: usize) -> &[u8] {
785 let row = self.rows.get(y).copied().unwrap_or(0) as usize;
786 self.samples
787 .get(row * self.stride..)
788 .and_then(|rest| rest.get(..self.stride))
789 .unwrap_or(&[])
790 }
791
792 fn sample(&self, row: &[u8], x: usize) -> u8 {
794 let column = self.columns.get(x).copied().unwrap_or(0) as usize;
795 row.get(column).copied().unwrap_or(0)
796 }
797}
798
799fn decode_block<S: Source>(
801 reader: &mut Reader<S>,
802 dc: &HuffmanTable,
803 ac: &HuffmanTable,
804 quant: &[u16; 64],
805 predictor: &mut i32,
806 out: &mut [i32; 64],
807) -> Result<()> {
808 out.fill(0);
809
810 let magnitude = reader.decode(dc)?;
811 if magnitude > 15 {
812 return Err(PixelsError::malformed(
813 "jpeg",
814 format!("DC coefficient claims {magnitude} bits; 15 is the maximum"),
815 ));
816 }
817 let difference = reader.receive_extend(u32::from(magnitude))?;
818 *predictor = predictor.wrapping_add(difference);
823 if let (Some(slot), Some(&step)) = (out.first_mut(), quant.first()) {
824 *slot = predictor.saturating_mul(i32::from(step));
825 }
826
827 let mut index = 1_usize;
828 while index < 64 {
829 let symbol = reader.decode(ac)?;
830 let (run, size) = ((symbol >> 4) as usize, u32::from(symbol & 0x0F));
831 if size == 0 {
832 if run != 15 {
833 break;
836 }
837 index += 16;
840 continue;
841 }
842 index += run;
843 if index > 63 {
844 return Err(PixelsError::malformed(
845 "jpeg",
846 "coefficient run passes the end of the block",
847 ));
848 }
849 let value = reader.receive_extend(size)?;
850 let step = quant.get(index).copied().unwrap_or(0);
851 if let Some(slot) = ZIGZAG.get(index).and_then(|&at| out.get_mut(at)) {
852 *slot = value.saturating_mul(i32::from(step));
853 }
854 index += 1;
855 }
856 Ok(())
857}
858
859pub(crate) fn ycbcr_to_rgb([y, cb, cr]: [u8; 3]) -> [u8; 3] {
865 const HALF: i32 = 1 << 15;
866 let luma = i32::from(y) << 16;
867 let blue = i32::from(cb) - 128;
868 let red = i32::from(cr) - 128;
869
870 let r = (luma + 91_881 * red + HALF) >> 16;
871 let g = (luma - 22_554 * blue - 46_802 * red + HALF) >> 16;
872 let b = (luma + 116_130 * blue + HALF) >> 16;
873 [
874 r.clamp(0, 255) as u8,
875 g.clamp(0, 255) as u8,
876 b.clamp(0, 255) as u8,
877 ]
878}
879
880fn colour_model(frame: &Frame, adobe: Option<AdobeTransform>) -> Result<Colour> {
882 match frame.components.len() {
883 1 => Ok(Colour::Grayscale),
884 3 => {
885 let labelled_rgb = frame.components.iter().map(|c| c.id).eq(*b"RGB");
888 Ok(if adobe == Some(AdobeTransform::None) || labelled_rgb {
889 Colour::Rgb
890 } else {
891 Colour::YCbCr
892 })
893 }
894 count => Err(PixelsError::unsupported(format!(
898 "jpeg: {count}-component images (CMYK/YCCK)"
899 ))),
900 }
901}
902
903fn check_baseline_scan(scan: &Scan) -> Result<()> {
906 if scan.spectral_start != 0 || scan.spectral_end != 63 {
907 return Err(PixelsError::malformed(
908 "jpeg",
909 format!(
910 "baseline scan selects coefficients {}..={}; it must select all 64",
911 scan.spectral_start, scan.spectral_end
912 ),
913 ));
914 }
915 if scan.approx_high != 0 || scan.approx_low != 0 {
916 return Err(PixelsError::malformed(
917 "jpeg",
918 "baseline scan uses successive approximation, which is progressive only",
919 ));
920 }
921 Ok(())
922}
923
924fn read_quantization_tables(payload: &[u8], quant: &mut [[u16; 64]; 4]) -> Result<()> {
926 let mut at = 0_usize;
927 while at < payload.len() {
928 let Some(&header) = payload.get(at) else {
929 break;
930 };
931 at += 1;
932 let (precision, slot) = (header >> 4, (header & 0x0F) as usize);
933 if slot > 3 {
934 return Err(PixelsError::malformed(
935 "jpeg",
936 format!("DQT names table {slot}; only 0..=3 exist"),
937 ));
938 }
939 let wide = match precision {
940 0 => false,
941 1 => true,
942 other => {
943 return Err(PixelsError::malformed(
944 "jpeg",
945 format!("DQT declares precision {other}; only 0 and 1 exist"),
946 ));
947 }
948 };
949
950 let Some(table) = quant.get_mut(slot) else {
951 break;
952 };
953 for entry in table.iter_mut() {
954 let value = if wide {
955 let (Some(&hi), Some(&lo)) = (payload.get(at), payload.get(at + 1)) else {
956 return Err(PixelsError::malformed("jpeg", "DQT segment is truncated"));
957 };
958 at += 2;
959 u16::from_be_bytes([hi, lo])
960 } else {
961 let Some(&value) = payload.get(at) else {
962 return Err(PixelsError::malformed("jpeg", "DQT segment is truncated"));
963 };
964 at += 1;
965 u16::from(value)
966 };
967 *entry = value;
968 }
969 }
970 Ok(())
971}
972
973fn read_huffman_tables(
975 payload: &[u8],
976 dc_tables: &mut [Option<HuffmanTable>; 4],
977 ac_tables: &mut [Option<HuffmanTable>; 4],
978) -> Result<()> {
979 let mut at = 0_usize;
980 while at < payload.len() {
981 let Some(&header) = payload.get(at) else {
982 break;
983 };
984 at += 1;
985 let (class, slot) = (header >> 4, (header & 0x0F) as usize);
986 if slot > 3 || class > 1 {
987 return Err(PixelsError::malformed(
988 "jpeg",
989 format!("DHT names class {class} table {slot}; classes are 0..=1, slots 0..=3"),
990 ));
991 }
992
993 let Some(counts) = payload.get(at..at + 16) else {
994 return Err(PixelsError::malformed(
995 "jpeg",
996 "DHT segment ends inside its code-length counts",
997 ));
998 };
999 let mut lengths = [0_u8; 16];
1000 lengths.copy_from_slice(counts);
1001 at += 16;
1002
1003 let total: usize = lengths.iter().map(|&c| c as usize).sum();
1004 let Some(values) = payload.get(at..at + total) else {
1005 return Err(PixelsError::malformed(
1006 "jpeg",
1007 "DHT segment ends inside its symbol list",
1008 ));
1009 };
1010 at += total;
1011
1012 let table = HuffmanTable::new(&lengths, values.to_vec())?;
1013 let slots = if class == 0 {
1014 &mut *dc_tables
1015 } else {
1016 &mut *ac_tables
1017 };
1018 if let Some(entry) = slots.get_mut(slot) {
1019 *entry = Some(table);
1020 }
1021 }
1022 Ok(())
1023}
1024
1025impl<S: Source + std::fmt::Debug> Decoder for Baseline<S> {
1026 fn descriptor(&self) -> ImageDescriptor {
1027 self.descriptor
1028 }
1029
1030 fn capability(&self) -> DecodeCapability {
1031 DecodeCapability::Sequential
1032 }
1033
1034 fn reduced_descriptor(&self, target: (u32, u32)) -> Option<ImageDescriptor> {
1035 if self.row > 0 || self.mcu_row > 0 {
1037 return None;
1038 }
1039 let full = (u32::from(self.frame.width), u32::from(self.frame.height));
1040 let scale = Scale::fitting(full, target);
1041 if scale == self.scale {
1042 return None;
1043 }
1044 ImageDescriptor::new(
1045 scale.apply(full.0),
1046 scale.apply(full.1),
1047 self.descriptor.pixel,
1048 )
1049 .ok()
1050 }
1051
1052 fn reduce_to(&mut self, descriptor: ImageDescriptor) -> Result<()> {
1053 let full = (u32::from(self.frame.width), u32::from(self.frame.height));
1054 let scale = Scale::ALL
1058 .into_iter()
1059 .find(|scale| {
1060 scale.apply(full.0) == descriptor.width && scale.apply(full.1) == descriptor.height
1061 })
1062 .ok_or_else(|| {
1063 PixelsError::invalid_argument(
1064 "descriptor",
1065 format!(
1066 "{}x{} is not an M/8 scale of {}x{}",
1067 descriptor.width, descriptor.height, full.0, full.1
1068 ),
1069 )
1070 })?;
1071 self.apply_scale(scale)
1072 }
1073
1074 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
1075 if self.row >= self.descriptor.height {
1076 return Err(PixelsError::invalid_argument(
1077 "out",
1078 format!("all {} rows have already been read", self.descriptor.height),
1079 ));
1080 }
1081 let row_bytes = self.descriptor.row_bytes();
1082 if out.len() != row_bytes {
1083 return Err(PixelsError::invalid_argument(
1084 "out",
1085 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
1086 ));
1087 }
1088
1089 if self.band_row >= self.band_height {
1090 self.fill_band()?;
1091 }
1092 let start = self.band_row as usize * row_bytes;
1093 let row = self
1094 .band
1095 .get(start..)
1096 .and_then(|rest| rest.get(..row_bytes))
1097 .ok_or_else(|| PixelsError::malformed("jpeg", "MCU row band is short"))?;
1098 out.copy_from_slice(row);
1099 self.band_row += 1;
1100 self.row += 1;
1101 Ok(())
1102 }
1103}
1104
1105#[must_use]
1109pub fn probe(prefix: &[u8]) -> bool {
1110 prefix.get(..3) == Some(&crate::format::SIGNATURE[..])
1111}
1112
1113#[derive(Debug, Clone, Copy, Default)]
1115pub struct JpegCodec;
1116
1117impl Codec for JpegCodec {
1118 fn format(&self) -> Format {
1119 Format::Jpeg
1120 }
1121
1122 fn magic_len(&self) -> usize {
1123 3
1124 }
1125
1126 fn probe(&self, prefix: &[u8]) -> bool {
1127 probe(prefix)
1128 }
1129}