Start · Sprachen · JavaScript · Referenz · CanvasRenderingContext2D.putImageData()

CanvasRenderingContext2D.putImageData()

Methode

Schreibt die Pixeldaten eines <code>ImageData</code>-Objekts direkt an die angegebene Position auf die Leinwand, optional nur einen rechteckigen Teilbereich („dirty rectangle“).

seit JavaScript Baseline: alle Browser Kategorie: webapi

Signatur

CanvasRenderingContext2D.prototype.putImageData(imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight)

Beschreibung

putImageData() ist das Gegenstück zu getImageData(): Es überträgt rohe RGBA-Pixeldaten aus einem ImageData-Objekt 1:1 in den Bitmap-Speicher des Canvas. So lassen sich Pixelmanipulationen wie Graustufen-Filter, Invertierung, Schwellwerte, prozedurale Texturen oder Partikel-Effekte umsetzen, bei denen jeder Pixel einzeln berechnet wird.

Wichtig: Die Methode arbeitet am Zeichenzustand vorbei. Transformationen (translate, scale, rotate), globalAlpha, globalCompositeOperation, Schatten, Filter und Clipping-Pfade werden ignoriert. Die Pixel werden nicht mit dem vorhandenen Inhalt gemischt, sondern ersetzen ihn – auch transparente Pixel überschreiben also das Darunterliegende.

Mit den optionalen Parametern dirtyX, dirtyY, dirtyWidth und dirtyHeight kann man nur einen Ausschnitt des ImageData schreiben. Das ist nützlich, wenn sich in einem großen Puffer nur ein kleiner Bereich geändert hat. Die Dirty-Koordinaten beziehen sich auf das ImageData, nicht auf den Canvas; der Ausschnitt landet an Position dx + dirtyX, dy + dirtyY.

Wer Bilddaten mit Transformation oder Alpha-Blending zeichnen möchte, sollte stattdessen ein ImageBitmap (via createImageBitmap()) oder einen Offscreen-Canvas erzeugen und diesen mit drawImage() zeichnen.

Parameter

Name Typ Default Beschreibung
$imageData Pflicht ImageData Das ImageData-Objekt mit den RGBA-Pixelwerten (je 4 Bytes pro Pixel in einem Uint8ClampedArray).
$dx Pflicht number Horizontale Zielposition (in Gerätepixeln) auf dem Canvas, an der die linke obere Ecke des ImageData platziert wird.
$dy Pflicht number Vertikale Zielposition auf dem Canvas.
$dirtyX number 0 X-Koordinate des zu schreibenden Ausschnitts innerhalb des ImageData.
$dirtyY number 0 Y-Koordinate des zu schreibenden Ausschnitts innerhalb des ImageData.
$dirtyWidth number imageData.width Breite des Ausschnitts. Negative Werte sind erlaubt und spiegeln das Rechteck in Richtung Ursprung.
$dirtyHeight number imageData.height Höhe des Ausschnitts. Negative Werte sind erlaubt.

Beispiele

Graustufen-Filter auf ein gezeichnetes Bild anwenden

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

const img = new Image();
img.src = 'foto.jpg'; // muss same-origin oder CORS-freigegeben sein
await img.decode();

canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const { data } = imageData;

for (let i = 0; i < data.length; i += 4) {
  // Luminanz nach ITU-R BT.709
  const grau = 0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2];
  data[i] = data[i + 1] = data[i + 2] = grau;
  // data[i + 3] (Alpha) bleibt unverändert
}

ctx.putImageData(imageData, 0, 0);

Prozeduralen Farbverlauf Pixel für Pixel erzeugen

const ctx = document.querySelector('canvas').getContext('2d');
const breite = 256;
const hoehe = 256;
const imageData = ctx.createImageData(breite, hoehe);

for (let y = 0; y < hoehe; y++) {
  for (let x = 0; x < breite; x++) {
    const i = (y * breite + x) * 4;
    imageData.data[i] = x;       // Rot steigt nach rechts
    imageData.data[i + 1] = y;   // Grün steigt nach unten
    imageData.data[i + 2] = 128; // konstantes Blau
    imageData.data[i + 3] = 255; // voll deckend
  }
}

ctx.putImageData(imageData, 20, 20);
console.log(ctx.getImageData(20, 20, 1, 1).data);
Uint8ClampedArray(4) [0, 0, 128, 255]

Nur einen Teilbereich schreiben (Dirty Rectangle)

const ctx = document.querySelector('canvas').getContext('2d');
const puffer = ctx.createImageData(200, 200);

// Gesamten Puffer rot füllen
for (let i = 0; i < puffer.data.length; i += 4) {
  puffer.data.set([255, 0, 0, 255], i);
}

// Nur den Ausschnitt (50,50)-(100,100) des Puffers schreiben.
// Er landet auf dem Canvas bei (10 + 50, 10 + 50) = (60, 60).
ctx.putImageData(puffer, 10, 10, 50, 50, 50, 50);

console.log(ctx.getImageData(60, 60, 1, 1).data); // rot
console.log(ctx.getImageData(10, 10, 1, 1).data); // unberührt
Uint8ClampedArray(4) [255, 0, 0, 255] Uint8ClampedArray(4) [0, 0, 0, 0]

Transformationen werden ignoriert – Workaround mit ImageBitmap

const ctx = document.querySelector('canvas').getContext('2d');
const imageData = ctx.createImageData(50, 50);
imageData.data.fill(255); // weiße, deckende Pixel

ctx.translate(100, 100);
ctx.rotate(Math.PI / 4);

// Landet trotz Transformation unrotiert bei (0, 0):
ctx.putImageData(imageData, 0, 0);

// Mit Transformation zeichnen: erst in ImageBitmap umwandeln
const bitmap = await createImageBitmap(imageData);
ctx.drawImage(bitmap, -25, -25); // rotiert um (100, 100)

// Wichtig · Fallstricke

  • Kein Zeichenzustand: Transformationsmatrix, globalAlpha, globalCompositeOperation, filter, Schatten und Clipping haben keinen Einfluss. Pixel werden ersetzt, nicht überblendet – für Blending drawImage() mit einem ImageBitmap verwenden.
  • Koordinaten in Gerätepixeln: Bei HiDPI-Canvas (z. B. canvas.width = cssBreite * devicePixelRatio plus ctx.scale()) wirkt die Skalierung nicht auf putImageData(). Positionen müssen selbst umgerechnet werden.
  • Premultiplied Alpha: Browser speichern intern oft vormultiplizierte Alphawerte. Ein Rundlauf putImageData() → getImageData() liefert bei halbtransparenten Pixeln daher nicht zwingend exakt dieselben RGB-Werte.
  • Fehler: Ein TypeError wird geworfen, wenn imageData kein ImageData ist; ein InvalidStateError, wenn dessen Daten-Puffer abgekoppelt (detached) wurde, z. B. nach Übertragung an einen Worker.
  • Performance: Häufige Aufrufe mit großen Puffern sind teuer (GPU↔CPU-Synchronisation). Bei vielen Lesezugriffen getContext('2d', { willReadFrequently: true }) setzen und nach Möglichkeit Dirty Rectangles nutzen. Für schwere Pixelarbeit OffscreenCanvas im Worker oder WebGL in Betracht ziehen.
  • Sicherheit: putImageData() selbst ist unkritisch, das vorherige getImageData() wirft aber einen SecurityError, wenn der Canvas durch fremde Bilder ohne CORS-Freigabe „verunreinigt“ (tainted) ist.
  • Seit jeher in allen Browsern verfügbar; ebenso auf OffscreenCanvasRenderingContext2D.