/**
 * Decode an animated image (GIF / APNG / animated WebP / animated AVIF) into
 * a list of frames with cumulative timing using the browser's ImageDecoder
 * API. Used by the server-side export pipeline so that animated <img> elements
 * can be rendered frame-accurately under our virtual clock — native <img>
 * playback ignores virtual time, so without this every captured frame would
 * show the GIF stuck at frame 0.
 *
 * Live `/present` keeps using <img> directly; only the export pipeline opts
 * into the canvas-from-decoded-frames path.
 */
export interface DecodedFrame {
    bitmap: ImageBitmap;
    /** Frame display duration in ms. */
    durationMs: number;
}
export interface DecodedAnimatedImage {
    frames: DecodedFrame[];
    totalMs: number;
    width: number;
    height: number;
}
/** True if the URL/data-URL looks like an animated container we can decode.
 * We don't try to detect "animated PNG" vs "static PNG" upfront — the decoder
 * call below will return one frame for static images and that's fine. */
export declare function isLikelyAnimated(src: string): boolean;
/** Decode every frame of an animated image. Falls back to a single-frame
 * result for static images. Returns null when ImageDecoder is unavailable
 * (e.g. older browsers) so callers can degrade gracefully. */
export declare function decodeAnimatedImage(src: string): Promise<DecodedAnimatedImage | null>;
/** Pick the frame that should be visible at `elapsedMs` for a looping animation
 * of total `totalMs`. Returns the index. */
export declare function frameIndexAt(decoded: DecodedAnimatedImage, elapsedMs: number): number;
/** Draw the frame visible at `elapsedMs` into the given canvas. */
export declare function drawFrameAt(canvas: HTMLCanvasElement, decoded: DecodedAnimatedImage, elapsedMs: number): void;
