1/** A synchronous, borrowed frame. The producer releases its source after present returns. */
2export interface DeviceFrameSink {
3 readonly present: (source: CanvasImageSource, width: number, height: number) => boolean;
4}
5
6/** Retains the latest frame in a canvas; consumers can invalidate textures after each draw. */
7export function createCanvasFrameSink(
8 canvas: HTMLCanvasElement,
9 onFrame?: (width: number, height: number) => void,
10): DeviceFrameSink {
11 return {
12 present(source, width, height) {
13 const context = canvas.getContext("2d");
14 if (!context) return false;
15 if (canvas.width !== width || canvas.height !== height) {
16 canvas.width = width;
17 canvas.height = height;
18 }
19 context.drawImage(source, 0, 0, width, height);
20 onFrame?.(width, height);
21 return true;
22 },
23 };
24}