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//!
18//! # Progressive frames
19//!
20//! A progressive frame (`SOF2`) is opened the same way, up to its first scan.
21//! On the first row request every scan is decoded into a whole-image buffer of
22//! coefficients (`progressive`), and bands are then built from that buffer
23//! through the same inverse DCT, upsampling and colour conversion — so a
24//! progressive file decodes at reduced scale too.
25
26use crate::entropy::Reader;
27use crate::format::{
28    AdobeTransform, Frame, IccChunks, Scan, ZIGZAG, adobe_transform, exif_orientation, marker,
29};
30use crate::huffman::HuffmanTable;
31use crate::idct::{self, Scale};
32use crate::progressive::{Coefficients, MAX_SCANS, Tables};
33use otf_pixels_core::{
34    Codec, DecodeCapability, Decoder, Format, ImageDescriptor, Limits, Orientation, PixelFormat,
35    PixelsError, Result, Source,
36};
37
38/// The most blocks a single MCU may contain (ITU-T T.81 §B.2.3).
39///
40/// Sampling factors are capped at 4x4 each, so without this a crafted frame
41/// could ask for 48 blocks per MCU across three components. The format says
42/// ten; holding it to that bounds the work one MCU can cost.
43const MAX_BLOCKS_PER_MCU: u32 = 10;
44
45/// How the frame's components map onto colour.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47enum Colour {
48    /// One component: luminance only.
49    Grayscale,
50    /// Three components, transformed.
51    YCbCr,
52    /// Three components already in RGB order.
53    Rgb,
54}
55
56/// One component's plane for the MCU row currently being decoded.
57#[derive(Debug)]
58struct Plane {
59    /// Samples per row, including the padding that rounds up to whole blocks.
60    stride: usize,
61    /// Decoded samples for this band.
62    samples: Vec<u8>,
63    /// Output column to plane column, precomputed to keep upsampling out of
64    /// the inner loop.
65    columns: Vec<u32>,
66    /// Output row within the band to plane row.
67    rows: Vec<u32>,
68}
69
70/// Decodes a JPEG stream.
71///
72/// Baseline frames stream one MCU row at a time. Progressive frames decode
73/// their scans into a coefficient buffer first and are then served the same
74/// way; the distinction is not visible through [`Decoder`].
75#[derive(Debug)]
76pub struct JpegDecoder<S: Source> {
77    inner: Box<Engine<S>>,
78    /// The ICC profile from the `APP2` segments before the scan.
79    icc: Option<Vec<u8>>,
80    /// The EXIF block from the first EXIF `APP1` segment.
81    exif: Option<Vec<u8>>,
82}
83
84impl<S: Source> JpegDecoder<S> {
85    /// Read every header up to and including the scan header.
86    ///
87    /// No coefficient is decoded here, baseline or progressive, so this is the
88    /// `probe()`/metadata path as well as the start of a decode (SPEC
89    /// §Guarantees 3).
90    ///
91    /// # Errors
92    ///
93    /// Returns [`PixelsError::Malformed`] for a stream that is not a
94    /// well-formed JPEG, [`PixelsError::Unsupported`] for a variant this crate
95    /// does not handle (arithmetic coding, lossless, 12-bit, CMYK), or
96    /// [`PixelsError::LimitExceeded`] if the frame exceeds `limits`.
97    pub fn new(source: S, limits: Limits) -> Result<Self> {
98        Self::with_scale(source, limits, Scale::Full)
99    }
100
101    /// Read every header, and decode at a reduced resolution.
102    ///
103    /// At `M/8` scale the decoder inverse-transforms only the low-frequency
104    /// corner of each block, so a thumbnail costs a fraction of the arithmetic
105    /// and — the point of it — the full-resolution image is never
106    /// materialized. Entropy decoding is unchanged: every coefficient is still
107    /// read, because the format gives no way to skip one.
108    ///
109    /// [`Decoder::descriptor`] reports the *scaled* size, so a caller that
110    /// asks for a reduced decode is told what it will actually receive.
111    ///
112    /// # Errors
113    ///
114    /// As [`JpegDecoder::new`].
115    pub fn with_scale(source: S, limits: Limits, scale: Scale) -> Result<Self> {
116        let mut inner = Box::new(Engine::with_scale(source, limits, scale)?);
117        let icc = inner.icc.take();
118        let exif = inner.exif.take();
119        Ok(Self { inner, icc, exif })
120    }
121
122    /// The resolution this decoder produces, as eighths of full size.
123    #[must_use]
124    pub const fn scale(&self) -> Scale {
125        self.inner.scale
126    }
127
128    /// Whether this stream is a progressive frame.
129    #[must_use]
130    pub const fn is_progressive(&self) -> bool {
131        self.inner.progressive
132    }
133}
134
135impl<S: Source + std::fmt::Debug> Decoder for JpegDecoder<S> {
136    fn descriptor(&self) -> ImageDescriptor {
137        self.inner.descriptor()
138    }
139
140    /// The ICC profile, joined from its `APP2` segments.
141    fn icc_profile(&self) -> Option<&[u8]> {
142        self.icc.as_deref()
143    }
144
145    /// The EXIF block, from the first EXIF `APP1` segment.
146    fn exif(&self) -> Option<&[u8]> {
147        self.exif.as_deref()
148    }
149
150    /// The EXIF orientation, from the first EXIF `APP1` segment.
151    fn orientation(&self) -> Orientation {
152        self.inner.orientation.unwrap_or_default()
153    }
154
155    fn capability(&self) -> DecodeCapability {
156        self.inner.capability()
157    }
158
159    fn reduced_descriptor(&self, target: (u32, u32)) -> Option<ImageDescriptor> {
160        self.inner.reduced_descriptor(target)
161    }
162
163    fn reduce_to(&mut self, descriptor: ImageDescriptor) -> Result<()> {
164        self.inner.reduce_to(descriptor)
165    }
166
167    fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
168        self.inner.read_row(out)
169    }
170}
171
172/// The decoder proper, for baseline and progressive frames alike.
173struct Engine<S: Source> {
174    reader: Reader<S>,
175    descriptor: ImageDescriptor,
176    frame: Frame,
177    scan: Scan,
178    colour: Colour,
179    /// Quantization tables in zigzag order, by slot.
180    quant: [[u16; 64]; 4],
181    /// DC and AC Huffman tables, by slot.
182    dc_tables: [Option<HuffmanTable>; 4],
183    ac_tables: [Option<HuffmanTable>; 4],
184    /// MCUs between restart markers; zero means there are none.
185    restart_interval: u16,
186    /// MCUs left before the next restart marker is due.
187    restarts_left: u32,
188    /// The EXIF orientation tag, if the file carries one.
189    orientation: Option<Orientation>,
190    /// The ICC profile, until [`JpegDecoder`] takes it.
191    icc: Option<Vec<u8>>,
192    /// The EXIF block, until [`JpegDecoder`] takes it.
193    exif: Option<Vec<u8>>,
194    planes: Vec<Plane>,
195    /// One output-width row per component, reused for every row: the
196    /// component's samples at output resolution, ready for colour conversion.
197    expanded: Vec<Vec<u8>>,
198    /// Per-component DC predictor, reset at every restart.
199    predictors: Vec<i32>,
200    /// Interleaved output rows for the MCU row just decoded.
201    band: Vec<u8>,
202    /// The next row of `band` to serve; equal to the band height when spent.
203    band_row: u32,
204    /// Pixel height of one MCU row, and so of `band`.
205    band_height: u32,
206    mcus_per_line: u32,
207    mcu_rows: u32,
208    /// The next MCU row to decode.
209    mcu_row: u32,
210    /// How much of full resolution this decode produces.
211    scale: Scale,
212    /// Kept so a later re-scale can be re-checked against the same bounds.
213    limits: Limits,
214    /// Output rows already served.
215    row: u32,
216    /// Whether the frame is progressive (`SOF2`).
217    progressive: bool,
218    /// A progressive frame's coefficients, once its scans are decoded.
219    coefficients: Option<Coefficients>,
220    /// Each component's quantization table, as it stood at the component's
221    /// first scan; a progressive file may redefine a slot between scans.
222    latched: Vec<Option<[u16; 64]>>,
223}
224
225impl<S: Source> std::fmt::Debug for Engine<S> {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        f.debug_struct("Engine")
228            .field("descriptor", &self.descriptor)
229            .field("colour", &self.colour)
230            .field("components", &self.frame.components)
231            .field("scale", &self.scale)
232            .field("restart_interval", &self.restart_interval)
233            .field("orientation", &self.orientation)
234            .field("row", &self.row)
235            .finish_non_exhaustive()
236    }
237}
238
239impl<S: Source> Engine<S> {
240    /// Read every header up to and including the scan header.
241    ///
242    /// No coefficient is decoded here, so this is the `probe()`/metadata path
243    /// as well as the start of a decode (SPEC §Guarantees 3).
244    ///
245    /// # Errors
246    ///
247    /// Returns [`PixelsError::Malformed`] for a stream that is not a
248    /// well-formed baseline JPEG, [`PixelsError::Unsupported`] for a JPEG
249    /// variant this crate does not own (progressive, arithmetic-coded, 12-bit,
250    /// CMYK), or [`PixelsError::LimitExceeded`] if the frame exceeds `limits`.
251    /// Read every header, and decode at a reduced resolution.
252    ///
253    /// At `M/8` scale the decoder inverse-transforms only the low-frequency
254    /// corner of each block, so a thumbnail costs a fraction of the arithmetic
255    /// and — the point of it — the full-resolution image is never
256    /// materialized for the rest of the pipeline to carry. Entropy decoding is
257    /// unchanged: every coefficient is still read, because the format gives no
258    /// way to skip one.
259    ///
260    /// [`Decoder::descriptor`] reports the *scaled* size, so a caller that
261    /// asks for a reduced decode is told what it will actually receive.
262    ///
263    /// # Errors
264    ///
265    /// As [`JpegDecoder::new`].
266    fn with_scale(source: S, limits: Limits, scale: Scale) -> Result<Self> {
267        let mut reader = Reader::new(source);
268        let mut progressive = false;
269        let mut quant = [[0_u16; 64]; 4];
270        let mut dc_tables: [Option<HuffmanTable>; 4] = [None, None, None, None];
271        let mut ac_tables: [Option<HuffmanTable>; 4] = [None, None, None, None];
272        let mut restart_interval = 0_u16;
273        let mut orientation = None;
274        let mut exif: Option<Vec<u8>> = None;
275        let mut icc = IccChunks::default();
276        let mut adobe = None;
277        let mut frame: Option<Frame> = None;
278
279        let first = reader.next_marker()?;
280        if first != marker::SOI {
281            return Err(PixelsError::malformed(
282                "jpeg",
283                format!("stream begins with marker {first:#04x}, not SOI"),
284            ));
285        }
286
287        let scan = loop {
288            let code = reader.next_marker()?;
289            match code {
290                marker::SOF0 | marker::SOF1 | marker::SOF2 => {
291                    if frame.is_some() {
292                        return Err(PixelsError::malformed(
293                            "jpeg",
294                            "stream declares more than one frame",
295                        ));
296                    }
297                    progressive = code == marker::SOF2;
298                    let parsed = Frame::parse(&reader.read_segment()?)?;
299                    // Checked here rather than once the scan header arrives,
300                    // so an enormous frame costs one segment parse and stops
301                    // (SPEC §Safety and limits).
302                    limits.check(u32::from(parsed.width), u32::from(parsed.height))?;
303                    frame = Some(parsed);
304                }
305                marker::DAC => {
306                    return Err(PixelsError::unsupported(
307                        "jpeg: arithmetic coding; baseline JPEG is Huffman coded",
308                    ));
309                }
310                code if marker::is_frame(code) => {
311                    return Err(PixelsError::unsupported(format!(
312                        "jpeg: frame type {code:#04x} is not baseline"
313                    )));
314                }
315                marker::DQT => read_quantization_tables(&reader.read_segment()?, &mut quant)?,
316                marker::DHT => {
317                    read_huffman_tables(&reader.read_segment()?, &mut dc_tables, &mut ac_tables)?;
318                }
319                marker::DRI => {
320                    let payload = reader.read_segment()?;
321                    let (Some(&hi), Some(&lo)) = (payload.first(), payload.get(1)) else {
322                        return Err(PixelsError::malformed(
323                            "jpeg",
324                            "DRI segment carries no interval",
325                        ));
326                    };
327                    restart_interval = u16::from_be_bytes([hi, lo]);
328                }
329                marker::APP1 => {
330                    let payload = reader.read_segment()?;
331                    // The first EXIF block wins; later ones are thumbnails.
332                    orientation = orientation.or_else(|| exif_orientation(&payload));
333                    if exif.is_none() {
334                        exif = payload.strip_prefix(b"Exif\0\0").map(<[u8]>::to_vec);
335                    }
336                }
337                marker::APP2 => icc.push(&reader.read_segment()?),
338                marker::APP14 => {
339                    let payload = reader.read_segment()?;
340                    adobe = adobe_transform(&payload).or(adobe);
341                }
342                marker::SOS => {
343                    let Some(ref frame) = frame else {
344                        return Err(PixelsError::malformed(
345                            "jpeg",
346                            "scan begins before any frame header",
347                        ));
348                    };
349                    break Scan::parse(&reader.read_segment()?, frame)?;
350                }
351                marker::EOI => {
352                    return Err(PixelsError::malformed(
353                        "jpeg",
354                        "stream ends before any scan",
355                    ));
356                }
357                // Segments that carry no pixels are skipped, not rejected:
358                // JFIF density, colour profiles, comments and vendor
359                // extensions all live here, and refusing them would fail
360                // files every viewer opens.
361                marker::APP0..=marker::APP15 | marker::COM => reader.skip_segment()?,
362                code if marker::is_standalone(code) => {}
363                _ => reader.skip_segment()?,
364            }
365        };
366
367        let Some(frame) = frame else {
368            return Err(PixelsError::malformed("jpeg", "stream has no frame header"));
369        };
370        // A progressive scan is checked when it is decoded, against the
371        // tables as they stand by then; a baseline one must be complete now.
372        if !progressive {
373            check_baseline_scan(&scan)?;
374        }
375
376        let colour = colour_model(&frame, adobe)?;
377        let pixel = match colour {
378            Colour::Grayscale => PixelFormat::Gray8,
379            Colour::YCbCr | Colour::Rgb => PixelFormat::Rgb8,
380        };
381        // The frame's own size drives the MCU grid — that is a property of the
382        // encoded data, not of how much of it we intend to produce — while the
383        // descriptor reports what the caller will actually receive.
384        let (full_width, full_height) = (u32::from(frame.width), u32::from(frame.height));
385        let descriptor = ImageDescriptor::with_limits(
386            scale.apply(full_width),
387            scale.apply(full_height),
388            pixel,
389            &limits,
390        )?;
391
392        if !progressive && scan.components.len() != frame.components.len() {
393            // Every component of a baseline frame is coded in one interleaved
394            // scan in practice. The alternative — a scan per component — needs
395            // whole-image component planes, which would trade the memory
396            // guarantee for a case no encoder in circulation produces.
397            return Err(PixelsError::unsupported(
398                "jpeg: non-interleaved baseline scan",
399            ));
400        }
401        for component in scan.components.iter().filter(|_| !progressive) {
402            let slot = frame
403                .components
404                .get(component.index)
405                .map_or(0, |c| c.quant as usize);
406            if quant
407                .get(slot)
408                .is_none_or(|table| table.iter().all(|&q| q == 0))
409            {
410                return Err(PixelsError::malformed(
411                    "jpeg",
412                    format!("scan uses quantization table {slot}, which was never defined"),
413                ));
414            }
415            if dc_tables
416                .get(component.dc as usize)
417                .is_none_or(Option::is_none)
418                || ac_tables
419                    .get(component.ac as usize)
420                    .is_none_or(Option::is_none)
421            {
422                return Err(PixelsError::malformed(
423                    "jpeg",
424                    format!(
425                        "scan uses Huffman tables {}/{}, which were never defined",
426                        component.dc, component.ac
427                    ),
428                ));
429            }
430        }
431
432        let (h_max, v_max) = (u32::from(frame.h_max()), u32::from(frame.v_max()));
433        let blocks: u32 = frame
434            .components
435            .iter()
436            .map(|c| u32::from(c.h) * u32::from(c.v))
437            .sum();
438        if blocks > MAX_BLOCKS_PER_MCU {
439            return Err(PixelsError::malformed(
440                "jpeg",
441                format!(
442                    "an MCU would hold {blocks} blocks; the format allows {MAX_BLOCKS_PER_MCU}"
443                ),
444            ));
445        }
446
447        let mcus_per_line = full_width.div_ceil(h_max * 8);
448        let mcu_rows = full_height.div_ceil(v_max * 8);
449
450        // Geometry is set up by `apply_scale`, which the planner may call
451        // again later: shrink-on-load decides the scale only once the whole
452        // pipeline is known, by which point this decoder already exists.
453        let mut decoder = Self {
454            reader,
455            descriptor,
456            predictors: vec![0; frame.components.len()],
457            frame,
458            scan,
459            colour,
460            quant,
461            dc_tables,
462            ac_tables,
463            restart_interval,
464            restarts_left: u32::from(restart_interval),
465            orientation,
466            icc: icc.assemble(),
467            exif,
468            limits,
469            planes: Vec::new(),
470            expanded: Vec::new(),
471            band: Vec::new(),
472            band_row: 0,
473            band_height: 0,
474            mcus_per_line,
475            mcu_rows,
476            mcu_row: 0,
477            scale: Scale::Full,
478            row: 0,
479            progressive,
480            coefficients: None,
481            latched: Vec::new(),
482        };
483        decoder.apply_scale(scale)?;
484        Ok(decoder)
485    }
486
487    /// Set the resolution this decoder will produce, rebuilding the geometry
488    /// that depends on it.
489    ///
490    /// Every plane dimension is in samples-per-block, so the scale is the only
491    /// thing the rest of the decoder has to know about.
492    ///
493    /// # Errors
494    ///
495    /// Returns [`PixelsError::InvalidArgument`] if any row has already been
496    /// read — a scaled decode cannot start halfway — or
497    /// [`PixelsError::LimitExceeded`] if the scaled size is outside `limits`.
498    fn apply_scale(&mut self, scale: Scale) -> Result<()> {
499        if self.row > 0 || self.mcu_row > 0 {
500            return Err(PixelsError::invalid_argument(
501                "scale",
502                format!("{} rows have already been decoded", self.row),
503            ));
504        }
505        let full_width = u32::from(self.frame.width);
506        let full_height = u32::from(self.frame.height);
507        let (h_max, v_max) = (u32::from(self.frame.h_max()), u32::from(self.frame.v_max()));
508        let descriptor = ImageDescriptor::with_limits(
509            scale.apply(full_width),
510            scale.apply(full_height),
511            self.descriptor.pixel,
512            &self.limits,
513        )?;
514
515        // How many samples one block becomes: 8 at full resolution, fewer at a
516        // reduced scale.
517        let sample = scale.block_size();
518        let band_height = v_max * sample;
519
520        let mut planes = Vec::with_capacity(self.frame.components.len());
521        for component in &self.frame.components {
522            let (h, v) = (u32::from(component.h), u32::from(component.v));
523            let stride = usize::try_from(self.mcus_per_line * h * sample)
524                .map_err(|_| PixelsError::malformed("jpeg", "component plane overflows"))?;
525            let height = usize::try_from(v * sample).unwrap_or(32);
526            let samples = stride
527                .checked_mul(height)
528                .ok_or_else(|| PixelsError::malformed("jpeg", "component plane overflows"))?;
529            planes.push(Plane {
530                stride,
531                samples: vec![0_u8; samples],
532                // Nearest-neighbour upsampling: output column `x` reads the
533                // component sample covering it. Triangle ("fancy") upsampling
534                // is smoother and is what libjpeg defaults to, which is why
535                // reference comparisons are made against a decoder configured
536                // to match rather than against libjpeg's default.
537                columns: (0..descriptor.width).map(|x| x * h / h_max).collect(),
538                rows: (0..band_height).map(|y| y * v / v_max).collect(),
539            });
540        }
541
542        let band = descriptor
543            .row_bytes()
544            .checked_mul(band_height as usize)
545            .ok_or_else(|| PixelsError::malformed("jpeg", "MCU row band overflows"))?;
546
547        self.descriptor = descriptor;
548        self.expanded = vec![vec![0_u8; descriptor.width as usize]; planes.len()];
549        self.planes = planes;
550        self.band = vec![0_u8; band];
551        // Nothing decoded yet, so the band is spent and the first `read_row`
552        // fills it.
553        self.band_row = band_height;
554        self.band_height = band_height;
555        self.scale = scale;
556        Ok(())
557    }
558
559    /// Decode one MCU row into the component planes, then convert it into the
560    /// interleaved output band.
561    fn fill_band(&mut self) -> Result<()> {
562        if self.mcu_row >= self.mcu_rows {
563            return Err(PixelsError::malformed(
564                "jpeg",
565                "more rows were requested than the frame declares",
566            ));
567        }
568
569        if self.progressive {
570            return self.fill_band_progressive();
571        }
572
573        let mut coefficients = [0_i32; 64];
574        let scale = self.scale;
575        let sample = scale.block_size() as usize;
576        for mcu in 0..self.mcus_per_line {
577            if self.restart_interval > 0 && self.restarts_left == 0 {
578                if !self.reader.restart()? {
579                    let met = self.reader.pending_marker();
580                    return Err(PixelsError::malformed(
581                        "jpeg",
582                        match met {
583                            Some(code) => format!(
584                                "expected a restart marker between MCU intervals, met {code:#04x}"
585                            ),
586                            None => "expected a restart marker between MCU intervals".to_owned(),
587                        },
588                    ));
589                }
590                // Predictors are differential *within* an interval; that is
591                // the whole point of restarts, and carrying them across one
592                // would corrupt every block after a resynchronization.
593                self.predictors.iter_mut().for_each(|p| *p = 0);
594                self.restarts_left = u32::from(self.restart_interval);
595            }
596
597            for scanned in &self.scan.components {
598                let Some(component) = self.frame.components.get(scanned.index) else {
599                    continue;
600                };
601                let (h, v) = (u32::from(component.h), u32::from(component.v));
602                let (Some(dc), Some(ac)) = (
603                    self.dc_tables
604                        .get(scanned.dc as usize)
605                        .and_then(Option::as_ref),
606                    self.ac_tables
607                        .get(scanned.ac as usize)
608                        .and_then(Option::as_ref),
609                ) else {
610                    return Err(PixelsError::malformed(
611                        "jpeg",
612                        "scan names a Huffman table that was never defined",
613                    ));
614                };
615                let quant = self
616                    .quant
617                    .get(component.quant as usize)
618                    .ok_or_else(|| PixelsError::malformed("jpeg", "component names no table"))?;
619                let Some(predictor) = self.predictors.get_mut(scanned.index) else {
620                    continue;
621                };
622                let Some(plane) = self.planes.get_mut(scanned.index) else {
623                    continue;
624                };
625
626                for block_y in 0..v {
627                    for block_x in 0..h {
628                        decode_block(
629                            &mut self.reader,
630                            dc,
631                            ac,
632                            quant,
633                            predictor,
634                            &mut coefficients,
635                        )?;
636                        let x = ((mcu * h) + block_x) as usize * sample;
637                        let y = block_y as usize * sample;
638                        idct::scaled_block(
639                            &coefficients,
640                            scale,
641                            &mut plane.samples,
642                            y * plane.stride + x,
643                            plane.stride,
644                        );
645                    }
646                }
647            }
648
649            if self.restart_interval > 0 {
650                self.restarts_left = self.restarts_left.saturating_sub(1);
651            }
652        }
653
654        self.mcu_row += 1;
655        self.convert_band();
656        self.band_row = 0;
657        Ok(())
658    }
659
660    /// [`Engine::fill_band`] for a progressive frame: the band's blocks come
661    /// from the coefficient buffer, decoded in full on the first call.
662    fn fill_band_progressive(&mut self) -> Result<()> {
663        if self.coefficients.is_none() {
664            self.decode_scans()?;
665        }
666        let Some(stored) = self.coefficients.as_ref() else {
667            return Err(PixelsError::graph(
668                "jpeg coefficients vanished after decoding",
669            ));
670        };
671
672        let mut natural = [0_i32; 64];
673        let scale = self.scale;
674        let sample = scale.block_size() as usize;
675        let mcu_row = self.mcu_row as usize;
676        for (index, component) in self.frame.components.iter().enumerate() {
677            let (h, v) = (usize::from(component.h), usize::from(component.v));
678            // A component no scan ever reached has all-zero coefficients, so
679            // whichever table dequantizes them makes no difference.
680            let quant = self
681                .latched
682                .get(index)
683                .copied()
684                .flatten()
685                .or_else(|| self.quant.get(component.quant as usize).copied())
686                .unwrap_or([0; 64]);
687            let Some(plane) = self.planes.get_mut(index) else {
688                continue;
689            };
690            for block_y in 0..v {
691                for block_x in 0..h * self.mcus_per_line as usize {
692                    let Some(block) = stored.block(index, block_x, mcu_row * v + block_y) else {
693                        continue;
694                    };
695                    for ((&at, &value), &step) in ZIGZAG.iter().zip(block).zip(&quant) {
696                        if let Some(slot) = natural.get_mut(at) {
697                            *slot = i32::from(value) * i32::from(step);
698                        }
699                    }
700                    idct::scaled_block(
701                        &natural,
702                        scale,
703                        &mut plane.samples,
704                        block_y * sample * plane.stride + block_x * sample,
705                        plane.stride,
706                    );
707                }
708            }
709        }
710
711        self.mcu_row += 1;
712        self.convert_band();
713        self.band_row = 0;
714        Ok(())
715    }
716
717    /// Decode every scan of a progressive frame into the coefficient buffer.
718    ///
719    /// The first scan's header was read when the decoder was built. Tables
720    /// and restart intervals may be redefined between scans and take effect
721    /// for the scans after them; the frame ends at `EOI`, or where the data
722    /// runs out, which leaves a partial download showing what arrived.
723    fn decode_scans(&mut self) -> Result<()> {
724        let mut coefficients = Coefficients::new(&self.frame, self.mcus_per_line, self.mcu_rows)?;
725        let mut latched: Vec<Option<[u16; 64]>> = vec![None; self.frame.components.len()];
726        let mut scan = self.scan.clone();
727        let mut scans = 0;
728        'scans: loop {
729            scans += 1;
730            if scans > MAX_SCANS {
731                return Err(PixelsError::malformed(
732                    "jpeg",
733                    format!("progressive frame has more than {MAX_SCANS} scans"),
734                ));
735            }
736            // libjpeg's rule: a component's table is fixed by its first scan.
737            for scanned in &scan.components {
738                let Some(component) = self.frame.components.get(scanned.index) else {
739                    continue;
740                };
741                if let Some(slot) = latched.get_mut(scanned.index) {
742                    if slot.is_none() {
743                        *slot = self.quant.get(component.quant as usize).copied();
744                    }
745                }
746            }
747            let tables = Tables {
748                dc: &self.dc_tables,
749                ac: &self.ac_tables,
750                restart_interval: self.restart_interval,
751            };
752            crate::progressive::decode_scan(
753                &mut self.reader,
754                &self.frame,
755                &scan,
756                &tables,
757                &mut coefficients,
758            )?;
759            self.reader.align();
760
761            loop {
762                // A stream that ends between scans is a partial download:
763                // what arrived stands, as it does mid-scan.
764                let Ok(code) = self.reader.next_marker() else {
765                    break 'scans;
766                };
767                match code {
768                    marker::SOS => {
769                        scan = Scan::parse(&self.reader.read_segment()?, &self.frame)?;
770                        continue 'scans;
771                    }
772                    marker::EOI => break 'scans,
773                    marker::DHT => read_huffman_tables(
774                        &self.reader.read_segment()?,
775                        &mut self.dc_tables,
776                        &mut self.ac_tables,
777                    )?,
778                    marker::DQT => {
779                        read_quantization_tables(&self.reader.read_segment()?, &mut self.quant)?;
780                    }
781                    marker::DRI => {
782                        let payload = self.reader.read_segment()?;
783                        let (Some(&hi), Some(&lo)) = (payload.first(), payload.get(1)) else {
784                            return Err(PixelsError::malformed(
785                                "jpeg",
786                                "DRI segment carries no interval",
787                            ));
788                        };
789                        self.restart_interval = u16::from_be_bytes([hi, lo]);
790                    }
791                    code if marker::is_frame(code) => {
792                        return Err(PixelsError::malformed(
793                            "jpeg",
794                            "stream declares more than one frame",
795                        ));
796                    }
797                    // A stray restart marker between scans carries nothing.
798                    code if marker::is_standalone(code) => {}
799                    _ => self.reader.skip_segment()?,
800                }
801            }
802        }
803        self.coefficients = Some(coefficients);
804        self.latched = latched;
805        Ok(())
806    }
807
808    /// Upsample the component planes and write interleaved pixels into the
809    /// output band.
810    ///
811    /// Each component's row is first expanded to output width in a buffer of
812    /// its own, so colour conversion is one straight loop over equal-length
813    /// slices — which the compiler vectorizes — rather than a per-pixel
814    /// lookup into each plane.
815    fn convert_band(&mut self) {
816        let width = self.descriptor.width as usize;
817        let row_bytes = self.descriptor.row_bytes();
818        let h_max = usize::from(self.frame.h_max());
819
820        for y in 0..self.band_height as usize {
821            let Some(out) = self
822                .band
823                .get_mut(y * row_bytes..)
824                .and_then(|rest| rest.get_mut(..row_bytes))
825            else {
826                continue;
827            };
828            for ((plane, component), expanded) in self
829                .planes
830                .iter()
831                .zip(&self.frame.components)
832                .zip(self.expanded.iter_mut())
833            {
834                // Whole ratios only; anything else takes the general path.
835                let h = usize::from(component.h.max(1));
836                let ratio = if h_max % h == 0 { h_max / h } else { 0 };
837                plane.expand_row(y, ratio, expanded);
838            }
839
840            match self.colour {
841                Colour::Grayscale => {
842                    if let Some(grey) = self.expanded.first() {
843                        for (slot, &value) in out.iter_mut().zip(grey).take(width) {
844                            *slot = value;
845                        }
846                    }
847                }
848                Colour::Rgb => {
849                    let [a, b, c] =
850                        [0, 1, 2].map(|i| self.expanded.get(i).map_or(&[][..], Vec::as_slice));
851                    for (pixel, ((&r, &g), &b)) in
852                        out.chunks_exact_mut(3).zip(a.iter().zip(b).zip(c))
853                    {
854                        pixel.copy_from_slice(&[r, g, b]);
855                    }
856                }
857                Colour::YCbCr => {
858                    let [luma, cb, cr] =
859                        [0, 1, 2].map(|i| self.expanded.get(i).map_or(&[][..], Vec::as_slice));
860                    for (pixel, ((&y, &cb), &cr)) in
861                        out.chunks_exact_mut(3).zip(luma.iter().zip(cb).zip(cr))
862                    {
863                        pixel.copy_from_slice(&ycbcr_to_rgb([y, cb, cr]));
864                    }
865                }
866            }
867        }
868    }
869}
870
871impl Plane {
872    /// Write this plane's samples for output row `y` of the band into
873    /// `out`, one per output column. `ratio` is how many output columns one
874    /// sample covers, or 0 if that is not a whole number; 1 and 2 — every
875    /// common layout — take fast paths, and anything else goes through the
876    /// general column table.
877    fn expand_row(&self, y: usize, ratio: usize, out: &mut [u8]) {
878        let source = self.row(y);
879        match ratio {
880            1 => {
881                let count = out.len().min(source.len());
882                if let (Some(to), Some(from)) = (out.get_mut(..count), source.get(..count)) {
883                    to.copy_from_slice(from);
884                }
885            }
886            2 => {
887                // A fixed two-byte copy, not `fill`: on a slice that short,
888                // `fill` is a `memset` call per pixel pair.
889                let half = out.len() / 2;
890                let mut pairs = out.chunks_exact_mut(2);
891                for (pair, &value) in (&mut pairs).zip(source) {
892                    pair.copy_from_slice(&[value, value]);
893                }
894                // An odd width leaves one column, covered by the last sample.
895                if let ([last], Some(&value)) = (pairs.into_remainder(), source.get(half)) {
896                    *last = value;
897                }
898            }
899            _ => {
900                for (x, slot) in out.iter_mut().enumerate() {
901                    *slot = self.sample(source, x);
902                }
903            }
904        }
905    }
906
907    /// The plane row backing output row `y` of the band.
908    fn row(&self, y: usize) -> &[u8] {
909        let row = self.rows.get(y).copied().unwrap_or(0) as usize;
910        self.samples
911            .get(row * self.stride..)
912            .and_then(|rest| rest.get(..self.stride))
913            .unwrap_or(&[])
914    }
915
916    /// The sample of `row` covering output column `x`.
917    fn sample(&self, row: &[u8], x: usize) -> u8 {
918        let column = self.columns.get(x).copied().unwrap_or(0) as usize;
919        row.get(column).copied().unwrap_or(0)
920    }
921}
922
923/// Decode one 8x8 block, dequantizing into natural order as it goes.
924fn decode_block<S: Source>(
925    reader: &mut Reader<S>,
926    dc: &HuffmanTable,
927    ac: &HuffmanTable,
928    quant: &[u16; 64],
929    predictor: &mut i32,
930    out: &mut [i32; 64],
931) -> Result<()> {
932    out.fill(0);
933
934    let magnitude = reader.decode(dc)?;
935    if magnitude > 15 {
936        return Err(PixelsError::malformed(
937            "jpeg",
938            format!("DC coefficient claims {magnitude} bits; 15 is the maximum"),
939        ));
940    }
941    let difference = reader.receive_extend(u32::from(magnitude))?;
942    // DC is coded as a difference from the previous block of the same
943    // component. Wrapping keeps a crafted stream from panicking on overflow;
944    // the result is nonsense pixels, which is the correct outcome for
945    // nonsense input.
946    *predictor = predictor.wrapping_add(difference);
947    if let (Some(slot), Some(&step)) = (out.first_mut(), quant.first()) {
948        *slot = predictor.saturating_mul(i32::from(step));
949    }
950
951    let mut index = 1_usize;
952    while index < 64 {
953        let symbol = reader.decode(ac)?;
954        let (run, size) = ((symbol >> 4) as usize, u32::from(symbol & 0x0F));
955        if size == 0 {
956            if run != 15 {
957                // End of block: every remaining coefficient is zero, which is
958                // where JPEG gets most of its compression.
959                break;
960            }
961            // A run of sixteen zeros, coded because the run length field
962            // cannot express more than fifteen.
963            index += 16;
964            continue;
965        }
966        index += run;
967        if index > 63 {
968            return Err(PixelsError::malformed(
969                "jpeg",
970                "coefficient run passes the end of the block",
971            ));
972        }
973        let value = reader.receive_extend(size)?;
974        let step = quant.get(index).copied().unwrap_or(0);
975        if let Some(slot) = ZIGZAG.get(index).and_then(|&at| out.get_mut(at)) {
976            *slot = value.saturating_mul(i32::from(step));
977        }
978        index += 1;
979    }
980    Ok(())
981}
982
983/// Convert one YCbCr triple to RGB in fixed point.
984///
985/// The coefficients are JFIF's, scaled by 2^16. Fixed point rather than float
986/// for ADR-0011's reason: the same input has to produce the same byte on every
987/// target.
988///
989/// Each constant above 2^15 is split into a shift and a multiplier below it,
990/// so every product is 16 by 16 bits: the baseline x86-64 target (SSE2) has
991/// no 32-bit vector multiply but does have 16-bit ones, so this form
992/// vectorizes and the direct one does not. The results are identical.
993///
994/// | Constant | As |
995/// |---|---|
996/// | 91 881 | `(c << 16) + 26 345 c` |
997/// | 116 130 | `(c << 17) − 14 942 c` |
998/// | 46 802 | `(c << 16) − 18 734 c` |
999#[inline]
1000pub(crate) fn ycbcr_to_rgb([y, cb, cr]: [u8; 3]) -> [u8; 3] {
1001    const HALF: i32 = 1 << 15;
1002    let luma = (i32::from(y) << 16) + HALF;
1003    let blue = i32::from(i16::from(cb) - 128);
1004    let red = i32::from(i16::from(cr) - 128);
1005
1006    let r = (luma + (red << 16) + 26_345 * red) >> 16;
1007    let g = (luma - 22_554 * blue - (red << 16) + 18_734 * red) >> 16;
1008    let b = (luma + (blue << 17) - 14_942 * blue) >> 16;
1009    [
1010        r.clamp(0, 255) as u8,
1011        g.clamp(0, 255) as u8,
1012        b.clamp(0, 255) as u8,
1013    ]
1014}
1015
1016/// Decide what the frame's components mean.
1017fn colour_model(frame: &Frame, adobe: Option<AdobeTransform>) -> Result<Colour> {
1018    match frame.components.len() {
1019        1 => Ok(Colour::Grayscale),
1020        3 => {
1021            // Component ids 'R', 'G', 'B' are the other way a JPEG says it is
1022            // not YCbCr, and predate Adobe's marker.
1023            let labelled_rgb = frame.components.iter().map(|c| c.id).eq(*b"RGB");
1024            Ok(if adobe == Some(AdobeTransform::None) || labelled_rgb {
1025                Colour::Rgb
1026            } else {
1027                Colour::YCbCr
1028            })
1029        }
1030        // CMYK and YCCK need an ink model and usually an ICC profile to look
1031        // right; v1 is sRGB-assumed (SPEC §Pixel formats), so guessing here
1032        // would produce confidently wrong colour.
1033        count => Err(PixelsError::unsupported(format!(
1034            "jpeg: {count}-component images (CMYK/YCCK)"
1035        ))),
1036    }
1037}
1038
1039/// Reject a scan header that is not the single full-spectrum scan baseline
1040/// requires.
1041fn check_baseline_scan(scan: &Scan) -> Result<()> {
1042    if scan.spectral_start != 0 || scan.spectral_end != 63 {
1043        return Err(PixelsError::malformed(
1044            "jpeg",
1045            format!(
1046                "baseline scan selects coefficients {}..={}; it must select all 64",
1047                scan.spectral_start, scan.spectral_end
1048            ),
1049        ));
1050    }
1051    if scan.approx_high != 0 || scan.approx_low != 0 {
1052        return Err(PixelsError::malformed(
1053            "jpeg",
1054            "baseline scan uses successive approximation, which is progressive only",
1055        ));
1056    }
1057    Ok(())
1058}
1059
1060/// Read one `DQT` segment, which may define several tables.
1061fn read_quantization_tables(payload: &[u8], quant: &mut [[u16; 64]; 4]) -> Result<()> {
1062    let mut at = 0_usize;
1063    while at < payload.len() {
1064        let Some(&header) = payload.get(at) else {
1065            break;
1066        };
1067        at += 1;
1068        let (precision, slot) = (header >> 4, (header & 0x0F) as usize);
1069        if slot > 3 {
1070            return Err(PixelsError::malformed(
1071                "jpeg",
1072                format!("DQT names table {slot}; only 0..=3 exist"),
1073            ));
1074        }
1075        let wide = match precision {
1076            0 => false,
1077            1 => true,
1078            other => {
1079                return Err(PixelsError::malformed(
1080                    "jpeg",
1081                    format!("DQT declares precision {other}; only 0 and 1 exist"),
1082                ));
1083            }
1084        };
1085
1086        let Some(table) = quant.get_mut(slot) else {
1087            break;
1088        };
1089        for entry in table.iter_mut() {
1090            let value = if wide {
1091                let (Some(&hi), Some(&lo)) = (payload.get(at), payload.get(at + 1)) else {
1092                    return Err(PixelsError::malformed("jpeg", "DQT segment is truncated"));
1093                };
1094                at += 2;
1095                u16::from_be_bytes([hi, lo])
1096            } else {
1097                let Some(&value) = payload.get(at) else {
1098                    return Err(PixelsError::malformed("jpeg", "DQT segment is truncated"));
1099                };
1100                at += 1;
1101                u16::from(value)
1102            };
1103            *entry = value;
1104        }
1105    }
1106    Ok(())
1107}
1108
1109/// Read one `DHT` segment, which may define several tables.
1110fn read_huffman_tables(
1111    payload: &[u8],
1112    dc_tables: &mut [Option<HuffmanTable>; 4],
1113    ac_tables: &mut [Option<HuffmanTable>; 4],
1114) -> Result<()> {
1115    let mut at = 0_usize;
1116    while at < payload.len() {
1117        let Some(&header) = payload.get(at) else {
1118            break;
1119        };
1120        at += 1;
1121        let (class, slot) = (header >> 4, (header & 0x0F) as usize);
1122        if slot > 3 || class > 1 {
1123            return Err(PixelsError::malformed(
1124                "jpeg",
1125                format!("DHT names class {class} table {slot}; classes are 0..=1, slots 0..=3"),
1126            ));
1127        }
1128
1129        let Some(counts) = payload.get(at..at + 16) else {
1130            return Err(PixelsError::malformed(
1131                "jpeg",
1132                "DHT segment ends inside its code-length counts",
1133            ));
1134        };
1135        let mut lengths = [0_u8; 16];
1136        lengths.copy_from_slice(counts);
1137        at += 16;
1138
1139        let total: usize = lengths.iter().map(|&c| c as usize).sum();
1140        let Some(values) = payload.get(at..at + total) else {
1141            return Err(PixelsError::malformed(
1142                "jpeg",
1143                "DHT segment ends inside its symbol list",
1144            ));
1145        };
1146        at += total;
1147
1148        let table = HuffmanTable::new(&lengths, values.to_vec())?;
1149        let slots = if class == 0 {
1150            &mut *dc_tables
1151        } else {
1152            &mut *ac_tables
1153        };
1154        if let Some(entry) = slots.get_mut(slot) {
1155            *entry = Some(table);
1156        }
1157    }
1158    Ok(())
1159}
1160
1161impl<S: Source + std::fmt::Debug> Decoder for Engine<S> {
1162    fn descriptor(&self) -> ImageDescriptor {
1163        self.descriptor
1164    }
1165
1166    fn capability(&self) -> DecodeCapability {
1167        DecodeCapability::Sequential
1168    }
1169
1170    fn reduced_descriptor(&self, target: (u32, u32)) -> Option<ImageDescriptor> {
1171        // Past the first row the geometry is settled.
1172        if self.row > 0 || self.mcu_row > 0 {
1173            return None;
1174        }
1175        let full = (u32::from(self.frame.width), u32::from(self.frame.height));
1176        let scale = Scale::fitting(full, target);
1177        if scale == self.scale {
1178            return None;
1179        }
1180        ImageDescriptor::new(
1181            scale.apply(full.0),
1182            scale.apply(full.1),
1183            self.descriptor.pixel,
1184        )
1185        .ok()
1186    }
1187
1188    fn reduce_to(&mut self, descriptor: ImageDescriptor) -> Result<()> {
1189        let full = (u32::from(self.frame.width), u32::from(self.frame.height));
1190        // The descriptor has to be one this decoder could have offered, not an
1191        // arbitrary size: only the four M/8 scales are reachable without
1192        // resampling, which is the caller's job and not ours.
1193        let scale = Scale::ALL
1194            .into_iter()
1195            .find(|scale| {
1196                scale.apply(full.0) == descriptor.width && scale.apply(full.1) == descriptor.height
1197            })
1198            .ok_or_else(|| {
1199                PixelsError::invalid_argument(
1200                    "descriptor",
1201                    format!(
1202                        "{}x{} is not an M/8 scale of {}x{}",
1203                        descriptor.width, descriptor.height, full.0, full.1
1204                    ),
1205                )
1206            })?;
1207        self.apply_scale(scale)
1208    }
1209
1210    fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
1211        if self.row >= self.descriptor.height {
1212            return Err(PixelsError::invalid_argument(
1213                "out",
1214                format!("all {} rows have already been read", self.descriptor.height),
1215            ));
1216        }
1217        let row_bytes = self.descriptor.row_bytes();
1218        if out.len() != row_bytes {
1219            return Err(PixelsError::invalid_argument(
1220                "out",
1221                format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
1222            ));
1223        }
1224
1225        if self.band_row >= self.band_height {
1226            self.fill_band()?;
1227        }
1228        let start = self.band_row as usize * row_bytes;
1229        let row = self
1230            .band
1231            .get(start..)
1232            .and_then(|rest| rest.get(..row_bytes))
1233            .ok_or_else(|| PixelsError::malformed("jpeg", "MCU row band is short"))?;
1234        out.copy_from_slice(row);
1235        self.band_row += 1;
1236        self.row += 1;
1237        Ok(())
1238    }
1239}
1240
1241/// Whether `prefix` starts with a JPEG signature.
1242///
1243/// Detection is by magic bytes only (SPEC §Formats).
1244#[must_use]
1245pub fn probe(prefix: &[u8]) -> bool {
1246    prefix.get(..3) == Some(&crate::format::SIGNATURE[..])
1247}
1248
1249/// The JPEG entry in a sniffing registry.
1250#[derive(Debug, Clone, Copy, Default)]
1251pub struct JpegCodec;
1252
1253impl Codec for JpegCodec {
1254    fn format(&self) -> Format {
1255        Format::Jpeg
1256    }
1257
1258    fn magic_len(&self) -> usize {
1259        3
1260    }
1261
1262    fn probe(&self, prefix: &[u8]) -> bool {
1263        probe(prefix)
1264    }
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269    use super::*;
1270
1271    /// The split-constant form must equal the direct JFIF formula for every
1272    /// one of the 16.7 million inputs, not merely for typical ones.
1273    #[test]
1274    fn colour_conversion_matches_the_direct_formula_everywhere() {
1275        fn direct([y, cb, cr]: [u8; 3]) -> [u8; 3] {
1276            const HALF: i32 = 1 << 15;
1277            let luma = i32::from(y) << 16;
1278            let blue = i32::from(cb) - 128;
1279            let red = i32::from(cr) - 128;
1280            let r = (luma + 91_881 * red + HALF) >> 16;
1281            let g = (luma - 22_554 * blue - 46_802 * red + HALF) >> 16;
1282            let b = (luma + 116_130 * blue + HALF) >> 16;
1283            [r, g, b].map(|v| v.clamp(0, 255) as u8)
1284        }
1285        for y in 0..=255 {
1286            for cb in 0..=255 {
1287                for cr in 0..=255 {
1288                    assert_eq!(ycbcr_to_rgb([y, cb, cr]), direct([y, cb, cr]));
1289                }
1290            }
1291        }
1292    }
1293}