Signatur
Beschreibung
Die Methode decode() des ImageDecoder-Interfaces aus der WebCodecs API dekodiert einen einzelnen Frame eines Bildes, etwa ein JPEG, PNG, WebP, AVIF oder einen einzelnen Frame aus einem animierten GIF. Der Aufruf wird intern in eine Warteschlange eingereiht und asynchron abgearbeitet. Das zurückgegebene Promise wird mit einem Objekt erfüllt, das den dekodierten Frame als VideoFrame enthält (image) und über complete angibt, ob das Bild vollständig dekodiert ist.
Gebraucht wird decode(), wenn man mehr Kontrolle braucht als mit <img> oder createImageBitmap(). Man kann zum Beispiel einzelne Frames einer Animation gezielt abrufen, Bilder in einem Worker dekodieren oder Frames direkt in ein <canvas>, in WebGL/WebGPU oder in einen VideoEncoder weitergeben.
Mit der Option frameIndex wählt man den Frame bei animierten Formaten. Setzt man completeFramesOnly auf false, liefert der Decoder bei progressiven Formaten (z. B. progressives JPEG) aus einem gestreamten ReadableStream schon Zwischenstufen, deren Qualität schrittweise steigt. Jeder zurückgegebene VideoFrame belegt Ressourcen, zum Teil auf der GPU, und muss nach der Nutzung mit close() freigegeben werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | object | {} | Optionales Konfigurationsobjekt mit den Eigenschaften:
|
Rückgabewert
image enthält den dekodierten Frame als VideoFrame, complete ist true, wenn das Bild in voller Detailstufe vorliegt. Bei Fehlern wird das Promise abgelehnt, etwa mit RangeError bei ungültigem frameIndex, EncodingError bei defekten Daten oder InvalidStateError, wenn der Decoder geschlossen ist.Beispiele
Einzelbild dekodieren und auf ein Canvas zeichnen
const response = await fetch('/bilder/foto.jpg');
const decoder = new ImageDecoder({
data: response.body,
type: 'image/jpeg'
});
const { image, complete } = await decoder.decode();
console.log(image.displayWidth, image.displayHeight, complete);
const canvas = document.querySelector('canvas');
canvas.width = image.displayWidth;
canvas.height = image.displayHeight;
canvas.getContext('2d').drawImage(image, 0, 0);
// Ressourcen freigeben
image.close();
decoder.close();
Alle Frames eines animierten GIFs abspielen
const response = await fetch('/bilder/animation.gif');
const decoder = new ImageDecoder({ data: response.body, type: 'image/gif' });
// Warten, bis die Track-Informationen vorliegen
await decoder.tracks.ready;
await decoder.completed; // sicherstellen, dass alle Daten geladen sind
const frameCount = decoder.tracks.selectedTrack.frameCount;
const ctx = document.querySelector('canvas').getContext('2d');
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
for (let i = 0; i < frameCount; i++) {
const { image } = await decoder.decode({ frameIndex: i });
ctx.drawImage(image, 0, 0);
// duration ist in Mikrosekunden angegeben
const delay = (image.duration ?? 100_000) / 1000;
image.close();
await sleep(delay);
}
console.log(`${frameCount} Frames abgespielt`);
Progressives JPEG schrittweise anzeigen
const response = await fetch('/bilder/progressiv.jpg');
const decoder = new ImageDecoder({ data: response.body, type: 'image/jpeg' });
const ctx = document.querySelector('canvas').getContext('2d');
let complete = false;
while (!complete) {
const result = await decoder.decode({ completeFramesOnly: false });
ctx.drawImage(result.image, 0, 0);
result.image.close();
complete = result.complete;
console.log(complete ? 'fertig' : 'Zwischenstufe');
}
decoder.close();
Unterstützung und Format vorab prüfen
const type = 'image/avif';
if (!('ImageDecoder' in window)) {
console.log('WebCodecs ImageDecoder nicht verfügbar');
} else if (!(await ImageDecoder.isTypeSupported(type))) {
console.log(`${type} wird nicht unterstützt`);
} else {
const blob = await (await fetch('/bilder/bild.avif')).blob();
const decoder = new ImageDecoder({ data: await blob.arrayBuffer(), type });
try {
const { image } = await decoder.decode({ frameIndex: 0 });
console.log('Dekodiert:', image.codedWidth, 'x', image.codedHeight);
image.close();
} catch (err) {
console.error('Dekodierung fehlgeschlagen:', err.name);
} finally {
decoder.close();
}
}
// Wichtig · Fallstricke
- Speicher freigeben: Jeder gelieferte
VideoFramemuss mitimage.close()freigegeben werden. Tut man das nicht, sammeln sich Speicher- und GPU-Ressourcen an, und der Browser gibt Warnungen aus. - Frame-Anzahl:
frameIndexmuss kleiner alstracks.selectedTrack.frameCountsein, sonst wird das Promise mitRangeErrorabgelehnt. Bei gestreamten Daten kannframeCountnoch steigen. Aufdecoder.tracks.readybzw.decoder.completedwarten. - Geschlossener Decoder: Nach
close()oderreset()werden ausstehendedecode()-Promises abgelehnt (AbortErrorbzw.InvalidStateError). - Zeitangaben:
VideoFrame.durationundtimestampsind in Mikrosekunden angegeben, nicht in Millisekunden. - Kompatibilität:
ImageDecoderist nur in sicheren Kontexten (HTTPS) verfügbar, auch in Workern. Chromium-Browser unterstützen es ab Version 94, Firefox ab 133. Safari unterstützt es derzeit nicht. Vorher per Feature-Detection undImageDecoder.isTypeSupported()prüfen und als AusweichlösungcreateImageBitmap()verwenden. - CORS: Bilder von fremden Domains lassen sich nur dekodieren, wenn die Daten per
fetch()mit gültigen CORS-Headern geladen wurden.