/** * Freeze an animated GIF by *truncating* it, before the browser ever decodes it. * * ## Why a GIF is the hardest of the easy cases * * A GIF has been animating since it decoded. No CSS reaches it — `animation-*` * governs CSS animations and a GIF is not one — so the trick that pins every * keyframe on the page leaves a spinning logo spinning, and two runs a second * apart read two different images. * * Argos solves this in the page, and the design is careful: construct a *fresh* * `Image`, draw it to a canvas, take `toDataURL('image/png')` as frame zero, and * swap it into `src`. Fresh, because the `` already on the page has been * animating since load and there is no way to seek it back. It works, and it has * a failure mode its own documentation states — a cross-origin image without * CORS taints the canvas, `toDataURL` throws, and the GIF is left animating. * * ## Why doing it on the wire is strictly better * * Intercept the response and the problem changes shape. The bytes have not been * decoded yet, so there is no animation to seek back to zero and no second * decode to pay for. **Cross-origin stops mattering**, because the fulfilment is * ours: a canvas is tainted by what the page may read, and nothing here asks a * page to read anything. And the result is the original bytes minus some of * them, so the palette, the transparency, the dimensions and the compression are * exactly what the author shipped — where a canvas round-trip re-encodes through * RGBA and produces a PNG that is a different image from the one under test. * * ## What the truncation actually is * * A single-frame GIF is a valid GIF, and it is a prefix of the animated one. * The format is a header, a screen descriptor, an optional global colour table, * and then a stream of blocks — extensions, image descriptors — ending in a * trailer. Keep everything up to and including the **first image descriptor and * its pixel data**, append the trailer byte, and the file is complete and * static. No decoder, no encoder, no dependency, and no re-encoding. * * This is the whole reason it lives in a package that requires a browser rather * than one that requires an image codec: it does not need one. * * @see https://www.w3.org/Graphics/GIF/spec-gif89a.txt */ /** * The first frame of a GIF, as a GIF — or `null` when there is nothing to do. * * `null` for anything this cannot honestly improve: bytes that are not a GIF, * bytes that are a GIF with one frame already, and bytes it could not parse. * Three different reasons for the same answer, because the caller's move is the * same in all three — pass the response through untouched — and inventing an * output for a file we did not understand is how a stabilizer corrupts an asset. * * Never throws. This runs inside a request handler, and a stabilizer that can * take down a page load is worse than the flake it prevents. */ export declare function freezeGif(bytes: Uint8Array): Uint8Array | null; /** * Whether a response is a GIF, by header first and extension second. * * Content type is what the browser will believe, so it decides. The extension is * a fallback for a server that sends `application/octet-stream` — common enough * on object storage — and never an override, because a `.gif` served as `png` is * a PNG to every decoder that matters. */ export declare function isGifResponse(headers: Readonly>, url: string): boolean; //# sourceMappingURL=gif.d.ts.map