KiteImage
Display any image KiteImage can decode: and it decides on its own whether to animate. Feed it a GIF and it plays (correct delays, disposal compositing, NETSCAPE loop count, holding the last frame after a finite loop ends); feed it a PNG/BMP and it just draws. One composable, no format branching at call sites.
Decoding runs off the UI thread (Dispatchers.Default) and re-runs when data changes. Until it finishes (or on failure) the composable occupies its layout slot but draws nothing; failures also reach onError if provided.
Playback is KiteAnimatedImage: frame selection by elapsed frame-clock time, so long loops never drift and janky frames get skipped, not stretched.
animate is the escape hatch: false pins the first composited frame of an animated input (thumbnails, previews). Static inputs ignore it.
Display an already-decoded KiteBitmap (for callers running their own decode pipeline). Conversion to ImageBitmap is remembered per instance.