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.