Skip to main content

otf_pixels_codec_jpeg/
decoder.rs

1//! The baseline JPEG decoder.
2//!
3//! # Shape of the work
4//!
5//! A JPEG is a sequence of marker segments — quantization tables, Huffman
6//! tables, a frame header — followed by one scan of entropy-coded data.
7//! [`JpegDecoder::new`] reads up to and including the scan header, so
8//! everything the engine needs to plan a pipeline is known before a single
9//! coefficient is decoded.
10//!
11//! The scan is then consumed one **MCU row** at a time. An MCU (minimum coded
12//! unit) is the smallest group of blocks that covers the same rectangle in
13//! every component, which with 2x2 chroma subsampling is four luma blocks and
14//! one of each chroma: 16x16 pixels. Decoding a row of them fills a band of
15//! component planes, which is upsampled and colour-converted into interleaved
16//! output rows on the spot. Peak memory is that band, not the image.
17
18use 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
29/// The most blocks a single MCU may contain (ITU-T T.81 §B.2.3).
30///
31/// Sampling factors are capped at 4x4 each, so without this a crafted frame
32/// could ask for 48 blocks per MCU across three components. The format says
33/// ten; holding it to that bounds the work one MCU can cost.
34const MAX_BLOCKS_PER_MCU: u32 = 10;
35
36/// How the frame's components map onto colour.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38enum Colour {
39    /// One component: luminance only.
40    Grayscale,
41    /// Three components, transformed.
42    YCbCr,
43    /// Three components already in RGB order.
44    Rgb,
45}
46
47/// One component's plane for the MCU row currently being decoded.
48#[derive(Debug)]
49struct Plane {
50    /// Samples per row, including the padding that rounds up to whole blocks.
51    stride: usize,
52    /// Decoded samples for this band.
53    samples: Vec<u8>,
54    /// Output column to plane column, precomputed to keep upsampling out of
55    /// the inner loop.
56    columns: Vec<u32>,
57    /// Output row within the band to plane row.
58    rows: Vec<u32>,
59}
60
61/// What parsing the headers turned out to find.
62enum Parsed<S: Source> {
63    /// A baseline frame, decoded by this crate.
64    Baseline(Box<Baseline<S>>),
65    /// A progressive frame, to be handed to the wrapped decoder along with the
66    /// header bytes already consumed from the stream.
67    #[cfg(feature = "progressive")]
68    Progressive {
69        /// Everything read so far, so the stream can be replayed from zero.
70        replay: Vec<u8>,
71        /// The source, positioned after `replay`.
72        source: S,
73        /// Carried across the handover so a progressive photograph reports its
74        /// orientation like a baseline one does.
75        orientation: Option<Orientation>,
76        /// Carried likewise.
77        icc: Option<Vec<u8>>,
78    },
79}
80
81/// Decodes a JPEG stream.
82///
83/// Baseline frames are decoded by this crate, one MCU row at a time.
84/// Progressive frames are handed to a wrapped decoder (ADR-0004) and are
85/// internally buffered — the distinction is not visible through [`Decoder`],
86/// which is the point of the trait boundary.
87#[derive(Debug)]
88pub struct JpegDecoder<S: Source> {
89    inner: Inner<S>,
90    /// The ICC profile from the `APP2` segments before the scan.
91    icc: Option<Vec<u8>>,
92}
93
94/// Which decoder is actually running.
95#[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    /// Read every header up to and including the scan header.
104    ///
105    /// No coefficient is decoded here for a baseline frame, so this is the
106    /// `probe()`/metadata path as well as the start of a decode (SPEC
107    /// §Guarantees 3). A progressive frame is decoded in full, because it has
108    /// no prefix that yields a finished row.
109    ///
110    /// # Errors
111    ///
112    /// Returns [`PixelsError::Malformed`] for a stream that is not a
113    /// well-formed JPEG, [`PixelsError::Unsupported`] for a variant neither
114    /// this crate nor the wrapped decoder handles (arithmetic coding, 12-bit,
115    /// CMYK), or [`PixelsError::LimitExceeded`] if the frame exceeds `limits`.
116    pub fn new(source: S, limits: Limits) -> Result<Self> {
117        Self::with_scale(source, limits, Scale::Full)
118    }
119
120    /// Read every header, and decode at a reduced resolution.
121    ///
122    /// At `M/8` scale the decoder inverse-transforms only the low-frequency
123    /// corner of each block, so a thumbnail costs a fraction of the arithmetic
124    /// and — the point of it — the full-resolution image is never
125    /// materialized. Entropy decoding is unchanged: every coefficient is still
126    /// read, because the format gives no way to skip one.
127    ///
128    /// [`Decoder::descriptor`] reports the *scaled* size, so a caller that
129    /// asks for a reduced decode is told what it will actually receive.
130    ///
131    /// `scale` is ignored for a progressive frame, which the wrapped decoder
132    /// produces at full size; [`Decoder::descriptor`] then reports that size,
133    /// so the caller is never misled about what it is getting.
134    ///
135    /// # Errors
136    ///
137    /// As [`JpegDecoder::new`].
138    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    /// The resolution this decoder produces, as eighths of full size.
164    ///
165    /// Always [`Scale::Full`] for a progressive frame.
166    #[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    /// Whether this stream was progressive, and so decoded by the wrapped
176    /// codec rather than by this crate.
177    #[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    /// The ICC profile, joined from its `APP2` segments.
197    fn icc_profile(&self) -> Option<&[u8]> {
198        self.icc.as_deref()
199    }
200
201    /// The EXIF orientation, from the first EXIF `APP1` segment.
202    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            // The wrapped decoder has no reduced-scale path, so shrink-on-load
223            // does not fire for a progressive source.
224            #[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
248/// The baseline decoder: everything this crate implements itself.
249struct Baseline<S: Source> {
250    reader: Reader<S>,
251    descriptor: ImageDescriptor,
252    frame: Frame,
253    scan: Scan,
254    colour: Colour,
255    /// Quantization tables in zigzag order, by slot.
256    quant: [[u16; 64]; 4],
257    /// DC and AC Huffman tables, by slot.
258    dc_tables: [Option<HuffmanTable>; 4],
259    ac_tables: [Option<HuffmanTable>; 4],
260    /// MCUs between restart markers; zero means there are none.
261    restart_interval: u16,
262    /// MCUs left before the next restart marker is due.
263    restarts_left: u32,
264    /// The EXIF orientation tag, if the file carries one.
265    orientation: Option<Orientation>,
266    /// The ICC profile, until [`JpegDecoder`] takes it.
267    icc: Option<Vec<u8>>,
268    planes: Vec<Plane>,
269    /// Per-component DC predictor, reset at every restart.
270    predictors: Vec<i32>,
271    /// Interleaved output rows for the MCU row just decoded.
272    band: Vec<u8>,
273    /// The next row of `band` to serve; equal to the band height when spent.
274    band_row: u32,
275    /// Pixel height of one MCU row, and so of `band`.
276    band_height: u32,
277    mcus_per_line: u32,
278    mcu_rows: u32,
279    /// The next MCU row to decode.
280    mcu_row: u32,
281    /// How much of full resolution this decode produces.
282    scale: Scale,
283    /// Kept so a later re-scale can be re-checked against the same bounds.
284    limits: Limits,
285    /// Output rows already served.
286    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    /// Read every header up to and including the scan header.
305    ///
306    /// No coefficient is decoded here, so this is the `probe()`/metadata path
307    /// as well as the start of a decode (SPEC §Guarantees 3).
308    ///
309    /// # Errors
310    ///
311    /// Returns [`PixelsError::Malformed`] for a stream that is not a
312    /// well-formed baseline JPEG, [`PixelsError::Unsupported`] for a JPEG
313    /// variant this crate does not own (progressive, arithmetic-coded, 12-bit,
314    /// CMYK), or [`PixelsError::LimitExceeded`] if the frame exceeds `limits`.
315    /// Read every header, and decode at a reduced resolution.
316    ///
317    /// At `M/8` scale the decoder inverse-transforms only the low-frequency
318    /// corner of each block, so a thumbnail costs a fraction of the arithmetic
319    /// and — the point of it — the full-resolution image is never
320    /// materialized for the rest of the pipeline to carry. Entropy decoding is
321    /// unchanged: every coefficient is still read, because the format gives no
322    /// way to skip one.
323    ///
324    /// [`Decoder::descriptor`] reports the *scaled* size, so a caller that
325    /// asks for a reduced decode is told what it will actually receive.
326    ///
327    /// # Errors
328    ///
329    /// As [`JpegDecoder::new`].
330    fn with_scale(source: S, limits: Limits, scale: Scale) -> Result<Parsed<S>> {
331        let mut reader = Reader::new(source);
332        // Recorded only until the frame type is known; a baseline frame drops
333        // the tape immediately, so an ordinary decode buffers nothing.
334        #[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                    // Checked here rather than once the scan header arrives,
367                    // so an enormous frame costs one segment parse and stops
368                    // (SPEC §Safety and limits).
369                    limits.check(u32::from(parsed.width), u32::from(parsed.height))?;
370                    frame = Some(parsed);
371                }
372                marker::SOF2 => {
373                    // ADR-0004: progressive is a wrapped codec, not ours. The
374                    // decoders differ far more than the marker suggests — the
375                    // coefficients arrive in spectral bands across many scans,
376                    // which rules out the one-band-at-a-time streaming this
377                    // decoder is built around. So the stream is handed over
378                    // whole, replayed from the header already consumed.
379                    #[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                    // The first EXIF block wins; later ones are thumbnails.
422                    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                // Segments that carry no pixels are skipped, not rejected:
445                // JFIF density, colour profiles, comments and vendor
446                // extensions all live here, and refusing them would fail
447                // files every viewer opens.
448                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        // The frame's own size drives the MCU grid — that is a property of the
465        // encoded data, not of how much of it we intend to produce — while the
466        // descriptor reports what the caller will actually receive.
467        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            // Every component of a baseline frame is coded in one interleaved
477            // scan in practice. The alternative — a scan per component — needs
478            // whole-image component planes, which would trade the memory
479            // guarantee for a case no encoder in circulation produces.
480            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        // Geometry is set up by `apply_scale`, which the planner may call
534        // again later: shrink-on-load decides the scale only once the whole
535        // pipeline is known, by which point this decoder already exists.
536        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    /// Set the resolution this decoder will produce, rebuilding the geometry
566    /// that depends on it.
567    ///
568    /// Every plane dimension is in samples-per-block, so the scale is the only
569    /// thing the rest of the decoder has to know about.
570    ///
571    /// # Errors
572    ///
573    /// Returns [`PixelsError::InvalidArgument`] if any row has already been
574    /// read — a scaled decode cannot start halfway — or
575    /// [`PixelsError::LimitExceeded`] if the scaled size is outside `limits`.
576    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        // How many samples one block becomes: 8 at full resolution, fewer at a
594        // reduced scale.
595        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                // Nearest-neighbour upsampling: output column `x` reads the
611                // component sample covering it. Triangle ("fancy") upsampling
612                // is smoother and is what libjpeg defaults to, which is why
613                // reference comparisons are made against a decoder configured
614                // to match rather than against libjpeg's default.
615                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        // Nothing decoded yet, so the band is spent and the first `read_row`
629        // fills it.
630        self.band_row = band_height;
631        self.band_height = band_height;
632        self.scale = scale;
633        Ok(())
634    }
635
636    /// Decode one MCU row into the component planes, then convert it into the
637    /// interleaved output band.
638    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                // Predictors are differential *within* an interval; that is
664                // the whole point of restarts, and carrying them across one
665                // would corrupt every block after a resynchronization.
666                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    /// Upsample the component planes and write interleaved pixels into the
734    /// output band.
735    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    /// The plane row backing output row `y` of the band.
784    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    /// The sample of `row` covering output column `x`.
793    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
799/// Decode one 8x8 block, dequantizing into natural order as it goes.
800fn 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    // DC is coded as a difference from the previous block of the same
819    // component. Wrapping keeps a crafted stream from panicking on overflow;
820    // the result is nonsense pixels, which is the correct outcome for
821    // nonsense input.
822    *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                // End of block: every remaining coefficient is zero, which is
834                // where JPEG gets most of its compression.
835                break;
836            }
837            // A run of sixteen zeros, coded because the run length field
838            // cannot express more than fifteen.
839            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
859/// Convert one YCbCr triple to RGB in fixed point.
860///
861/// The coefficients are JFIF's, scaled by 2^16. Fixed point rather than float
862/// for ADR-0011's reason: the same input has to produce the same byte on every
863/// target.
864pub(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
880/// Decide what the frame's components mean.
881fn colour_model(frame: &Frame, adobe: Option<AdobeTransform>) -> Result<Colour> {
882    match frame.components.len() {
883        1 => Ok(Colour::Grayscale),
884        3 => {
885            // Component ids 'R', 'G', 'B' are the other way a JPEG says it is
886            // not YCbCr, and predate Adobe's marker.
887            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        // CMYK and YCCK need an ink model and usually an ICC profile to look
895        // right; v1 is sRGB-assumed (SPEC §Pixel formats), so guessing here
896        // would produce confidently wrong colour.
897        count => Err(PixelsError::unsupported(format!(
898            "jpeg: {count}-component images (CMYK/YCCK)"
899        ))),
900    }
901}
902
903/// Reject a scan header that is not the single full-spectrum scan baseline
904/// requires.
905fn 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
924/// Read one `DQT` segment, which may define several tables.
925fn 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
973/// Read one `DHT` segment, which may define several tables.
974fn 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        // Past the first row the geometry is settled.
1036        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        // The descriptor has to be one this decoder could have offered, not an
1055        // arbitrary size: only the four M/8 scales are reachable without
1056        // resampling, which is the caller's job and not ours.
1057        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/// Whether `prefix` starts with a JPEG signature.
1106///
1107/// Detection is by magic bytes only (SPEC §Formats).
1108#[must_use]
1109pub fn probe(prefix: &[u8]) -> bool {
1110    prefix.get(..3) == Some(&crate::format::SIGNATURE[..])
1111}
1112
1113/// The JPEG entry in a sniffing registry.
1114#[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}