Adds a from-scratch baseline JPEG decoder (ITU-T T.81, SOF0 sequential DCT),
so the Web app now displays the web's dominant photo format alongside PNG and
BMP. image_decode dispatches on the JPEG magic, so the browser needs no change
beyond that.
lib/jpeg.c handles grayscale and YCbCr, arbitrary chroma subsampling
(4:4:4/4:2:2/4:2:0), restart markers, and multiple DQT/DHT tables. Huffman
uses canonical tables (counts + symbols, like the inflate decoder) to avoid a
large fast-table; the transform is the standard fixed-point integer row/column
IDCT; colour is integer YCbCr->RGB. All heap-allocated, no floating point, so
it is safe in the freestanding kernel. Progressive (SOF2) and arithmetic-coded
JPEGs are rejected rather than mis-decoded.
Verified on the host against PIL-encoded references before going in the kernel,
the same way the crypto and PNG decoders were: grayscale decodes at mean
absolute error 0.9/channel, 4:4:4 at 1.7 (pure IDCT rounding vs libjpeg), and
4:2:0 at 4.3 (nearest-neighbour chroma upsampling softens colour edges -- a
known, visually-fine artifact). In QEMU the Web app fetched a 400x260 JPEG over
HTTP and rendered it correctly -- gradient, an antialiased red circle, a green
box, a yellow diagonal. Builds clean on x86_64, aarch64 and armv8i.
Limits: baseline only (no progressive), and chroma upsampling is
nearest-neighbour rather than the smoother interpolation libjpeg uses. The
browser still shows images by URL, not inline in a page.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
The Web app could only render text. Now it decodes and displays images, so a
picture on the web actually shows up.
- lib/inflate.c DEFLATE / zlib decompressor (RFC 1950/1951): the algorithm
every PNG needs. Canonical Huffman, all three block types,
decodes into a caller buffer -- no allocation, integer only.
- lib/image.c PNG decoder (8-bit RGB/RGBA/grayscale/gray+alpha, and
1/2/4/8-bit grayscale & palette, non-interlaced; tRNS
transparency) plus BMP (24/32-bit). Parses chunks,
inflates IDAT, undoes the five scanline filters, and expands
to a flat 0xAARRGGBB buffer ready for gui_gfx_blit. A pixel
cap guards the heap.
- Web app: if a response body sniffs as an image it is decoded once and drawn
instead of text -- scaled to fit the panel width, vertically scrollable,
alpha-composited over the background. The per-fetch cap grew to 128 KB so
real images and longer pages fit.
Both decoders were verified on the host against generated references before
going in the kernel: inflate round-trips six zlib/raw streams (empty, back-
references, dynamic Huffman, stored blocks, 20 KB random, mixed); the image
decoder reproduces PIL-made PNGs of every supported colour type -- grayscale,
RGB, RGBA, 4-bit palette, gray+alpha -- and a BMP, with exact pixel matches.
In QEMU the Web app fetched a 360x240 PNG over HTTP and rendered it perfectly:
gradient, an antialiased red circle, a green box, a yellow diagonal, shown as
"image 360x240". Builds clean on x86_64, aarch64 and armv8i.
Still image-by-URL, not inline <img> within a page (that needs sub-resource
fetching and layout); and no JPEG/GIF (different codecs). PNG and BMP cover a
large share of the web's images.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX