KiteAnimatedImage
Play an already-decoded KiteAnimation: the one frame loop shared by KiteImage's byte-array overload and kiteimage-coil's KiteAsyncImage. Public because callers running their own decode pipeline deserve playback too, exactly like the KiteImage overload that takes a KiteBitmap.
The frame to show is derived from elapsed frame-clock time against the animation's cumulative delays (not chained delay() calls), so scheduling latency never accumulates: playback can't drift slow over long loops, and a janky UI skips frames like every browser does instead of slowing the GIF down. Honors per-frame delays, the NETSCAPE loop count (0 = forever), and holds the last frame once a finite loop completes.
Frames convert to ImageBitmap lazily, once each, cached per animation instance. Static (single-frame) animations just draw. animate pins the first frame when false (thumbnails, previews).