feat: implement DEC Sixel graphics and the Kitty graphics protocol
Closes the last 3 failing probes in scripts/terminfo_dev_test.py's
conformance suite (108/111 -> 111/111): extensions.sixel,
extensions.sixel-da1, and extensions.kitty-graphics.
Both protocols share one storage model on vt.Grid (vt/image.odin):
a new Cell.image_id anchors a placement to a single cell, mirroring
link_id/cluster_id's "small id, resolved via a side table" shape but,
unlike those never-freed tables, bounded and evictable (a placed
image can be megabytes of raw RGBA). Sixel (vt/sixel.odin) is a
from-scratch decoder for the 6-dot-per-byte palette bitmap encoding.
Kitty graphics (vt/kitty_graphics.odin) leans on Odin's own core:
stdlib (image/png, encoding/base64, compress/zlib) for decoding, no
new FFI. render/render.odin gained a draw_row_images pass that blits
placed images into the same wl_shm pixel buffer, reusing the glyph
pass's existing alpha-compositing helper.
Deliberate scope cuts (each documented at its point in the code):
Kitty's raw RGB/RGBA formats, file/temp-file transmission mediums
(security -- same class of concern as OSC 8's url_scheme_allowed),
and re-placing a previously-transmitted image; sixel's HLS color
mode. See ROADMAP.md's v1 (done) entry for the full writeup,
including a real defer-scoping bug this work turned up along the way
(defer inside an if-block fires at the end of that block, not the
function).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>