Signatur
Beschreibung
ImageDecoder gehört zur WebCodecs API. Die Schnittstelle gibt dir direkten, einzelbildgenauen Zugriff auf kodierte Bilddaten. Anders als bei <img> oder createImageBitmap() kannst du damit gezielt einzelne Frames aus animierten Formaten (GIF, animiertes WebP/PNG, AVIF-Sequenzen) dekodieren, Bilder aus einem ReadableStream schon während des Downloads verarbeiten und die Zielgröße sowie die Farbraum-Umrechnung festlegen.
Einen Decoder erzeugst du mit new ImageDecoder(init). Dabei übergibst du den MIME-Typ und die Daten als ArrayBuffer, TypedArray, DataView oder ReadableStream. Wichtige Mitglieder:
decode({ frameIndex, completeFramesOnly })– liefert einPromise<{ image: VideoFrame, complete: boolean }>.tracks– eineImageTrackListmit Tracks;tracks.readyundtracks.selectedTrack.frameCount,animated,repetitionCount.completed(Promise) undcomplete(boolean) – zeigen an, ob die kodierten Daten vollständig geladen sind.type– der MIME-Typ, der beim Konstruieren angegeben wurde.reset()bricht laufendedecode()-Aufrufe ab,close()gibt alle Ressourcen endgültig frei.- Die statische Methode
ImageDecoder.isTypeSupported(type)prüft, ob ein Format unterstützt wird.
Typische Einsatzfälle sind eigene GIF- oder Animations-Player, Bildbearbeitung im Browser, das Erzeugen von Vorschaubildern in einem Worker und Pipelines, in denen die Frames an einen VideoEncoder oder an WebGL/WebGPU weitergereicht werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $init Pflicht | Object | Konfigurationsobjekt für den Konstruktor new ImageDecoder(init). |
|
| $init.type Pflicht | string | MIME-Typ der Bilddaten, z. B. "image/gif", "image/png" oder "image/webp". |
|
| $init.data Pflicht | ArrayBuffer | TypedArray | DataView | ReadableStream | Die kodierten Bilddaten. Mit einem ReadableStream lässt sich schon während des Downloads dekodieren. |
|
| $init.premultiplyAlpha | "none" | "premultiply" | "default" | "default" | Legt fest, ob die Farbkanäle mit dem Alphakanal vormultipliziert werden. |
| $init.colorSpaceConversion | "none" | "default" | "default" | Bei "none" werden eingebettete Farbprofile ignoriert, und es findet keine Farbraum-Umrechnung statt. |
| $init.desiredWidth | number | Gewünschte Ausgabebreite. Nur zusammen mit desiredHeight gültig; der Decoder darf skalieren. |
|
| $init.desiredHeight | number | Gewünschte Ausgabehöhe. Nur zusammen mit desiredWidth gültig. |
|
| $init.preferAnimation | boolean | Wenn es mehrere Tracks gibt: Bei true wird bevorzugt der animierte Track gewählt. |
|
| $init.transfer | ArrayBuffer[] | [] | Buffer, deren Besitz an den Decoder übergeht (ohne Kopie). Die Originale werden danach unbrauchbar (detached). |
Rückgabewert
ImageDecoder-Instanz zurück. decode() gibt ein Promise<ImageDecodeResult> mit image (VideoFrame) und complete (boolean) zurück.Beispiele
Erstes Bild dekodieren und auf ein Canvas zeichnen
const response = await fetch('/bilder/foto.jpg');
const decoder = new ImageDecoder({
type: 'image/jpeg',
data: response.body // ReadableStream: Dekodieren beginnt schon beim Laden
});
const { image } = await decoder.decode({ frameIndex: 0 });
const canvas = document.querySelector('canvas');
canvas.width = image.displayWidth;
canvas.height = image.displayHeight;
canvas.getContext('2d').drawImage(image, 0, 0);
console.log(`${image.displayWidth}×${image.displayHeight}`);
image.close(); // VideoFrame freigeben
decoder.close(); // Decoder freigeben
Animiertes GIF Frame für Frame abspielen
const canvas = document.querySelector('#player');
const ctx = canvas.getContext('2d');
const playGif = async (url) => {
const buffer = await (await fetch(url)).arrayBuffer();
const decoder = new ImageDecoder({ type: 'image/gif', data: buffer });
await decoder.tracks.ready;
await decoder.completed; // frameCount ist erst danach endgültig
const track = decoder.tracks.selectedTrack;
console.log('Frames:', track.frameCount, 'animiert:', track.animated);
for (let i = 0; i < track.frameCount; i++) {
const { image } = await decoder.decode({ frameIndex: i });
canvas.width = image.displayWidth;
canvas.height = image.displayHeight;
ctx.drawImage(image, 0, 0);
// duration ist in Mikrosekunden angegeben
const delay = (image.duration ?? 100_000) / 1000;
image.close();
await new Promise((r) => setTimeout(r, delay));
}
decoder.close();
};
playGif('/animationen/lade-spinner.gif');
Formatunterstützung prüfen mit Rückfall auf createImageBitmap
const decodeFirstFrame = async (blob) => {
if ('ImageDecoder' in window && await ImageDecoder.isTypeSupported(blob.type)) {
const decoder = new ImageDecoder({ type: blob.type, data: blob.stream() });
const { image } = await decoder.decode();
decoder.close();
return image; // VideoFrame
}
// Rückfall für Browser ohne WebCodecs (z. B. Safari)
return createImageBitmap(blob);
};
console.log(await ImageDecoder.isTypeSupported('image/avif'));
Vorschaubild in reduzierter Größe dekodieren
const file = document.querySelector('input[type=file]').files[0];
const decoder = new ImageDecoder({
type: file.type,
data: await file.arrayBuffer(),
desiredWidth: 320,
desiredHeight: 240
});
const { image, complete } = await decoder.decode({ completeFramesOnly: true });
console.log(complete, image.displayWidth, image.displayHeight);
image.close();
decoder.close();