Skip to content
io
jpeg

jpeg

Mojo module 🡭

jpeg

Read baseline and progressive JPEG files (ITU T.81), stdlib-only, with no libjpeg dependency.

A JPEG file is a sequence of marker segments – quantization tables (DQT), Huffman tables (DHT), the frame header (SOF), an optional restart interval (DRI), and the scan (SOS) whose entropy-coded data runs to the next marker – followed by EOI. decode_jpeg walks the segments, and for the scan decodes each minimum coded unit (MCU) in order: per component, v x h blocks of 64 quantized DCT coefficients, each a Huffman-coded DC difference and run-length coded AC terms in zigzag order, dequantized and put through the inverse DCT into the component’s sample plane. The planes are then resampled to the image grid and converted from YCbCr to RGB (JFIF’s equations) into an opaque Canvas. A chroma plane subsampled 2:1 horizontally, vertically or both is upsampled with the triangle filter libjpeg calls “fancy” (_upsample): each output sample is three parts the nearest input sample to one part the next nearest, with libjpeg’s rounding, so a file decodes to the same pixels here as through libjpeg to within the inverse DCT’s rounding. Other ratios take the nearest sample.

A progressive file (SOF2) is built the other way round. Its scans each carry part of every block – a band of the spectrum (Ss..Se), at a bit position (Ah/Al) – so no block is complete until the last scan, and the inverse DCT waits for all of them (_finish_progressive). Until then the coefficients live in each component’s coeffs. A DC scan may interleave components and is read in MCU order; an AC scan names one component and is read in that component’s own block raster, which for a subsampled component is narrower than the MCU-padded array the coefficients sit in. The refinement scans are T.81 G.1.2.3: every coefficient already non-zero in the band spends a correction bit whether or not the scan has anything new for it, so a run counts only the still-zero ones and the corrections are interleaved with it.

Scope: baseline, extended sequential and progressive Huffman processes (SOF0, SOF1, SOF2) at 8-bit precision, 1 or 3 components, any sampling factors, with or without restart intervals. Lossless, hierarchical, arithmetic-coded and 12-bit files raise with the reason, as does a four-component (CMYK) file. A file that ends without an EOI marker raises too: a truncated progressive stream otherwise decodes to a coarse but valid-looking image, which is a worse failure than an error. An Adobe APP14 segment declaring the three components as RGB rather than YCbCr is honored; EXIF orientation is not applied.

The Huffman decode is T.81 Annex F.2.2.3, with a nine-bit lookup ahead of it for the codes short enough to fit, the same shape as canvas.io.deflate’s. The bit reader unstuffs FF 00 and stops at a marker, so a truncated scan pads with zero bits rather than reading past its data.

Functions