Start · Sprachen · JavaScript · Referenz · ImageDecoder

ImageDecoder

Schnittstelle

Die WebCodecs-Schnittstelle <code>ImageDecoder</code> entpackt und dekodiert kodierte Bilddaten (z. B. JPEG, PNG, GIF, WebP, AVIF) Bild für Bild zu <code>VideoFrame</code>-Objekten.

seit JavaScript WebCodecs API – Chrome/Edge 94+, Firefox 133+; nic Kategorie: webapi

Signatur

interface ImageDecoder

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 ein Promise<{ image: VideoFrame, complete: boolean }>.
  • tracks – eine ImageTrackList mit Tracks; tracks.ready und tracks.selectedTrack.frameCount, animated, repetitionCount.
  • completed (Promise) und complete (boolean) – zeigen an, ob die kodierten Daten vollständig geladen sind.
  • type – der MIME-Typ, der beim Konstruieren angegeben wurde.
  • reset() bricht laufende decode()-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

Typ
ImageDecoder
Beschreibung
Der Konstruktor gibt eine neue 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
1920×1080

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');
Frames: 12 animiert: true

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'));
true

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();
true 320 240