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