Bitmaps – PNG und JPEG – Bildbearbeitung in Canvas
Bilder in das Canvas-Element laden
Zeichnen im Canvas ist eher eine rare Ausnahme. Der eigentlich Grund für die Existenz des Canvas-Elements ist das Laden und Anzeigen von Bildern.
const data = ctx.getImageData(0, 0, w, h);
Insbesondere kann JavaScript Bilder, die im Canvas bearbeitet wurden, zum Download anbieten: canvas.toblob()
Ein Bitmap laden und im Canvas anzeigen, um einen rechteckigen Rahmen auszuschneiden geht elegant mit Hilfe von drawImage() mit 9 Parametern.
ctx.drawImage( image, sx, sy, sw, sh, // Ausschnitt im Originalbild dx, dy, dw, dh // Ziel im Canvas – das Crop-Rechteck );
Der typische Workflow für das Laden eines Bildes ist
- Bild laden
- drawImage()
- Pixel bearbeiten
- toBlob() / Export
new Image() ▶ laden ▶ ctx.drawImage() – das ist der Standardweg (entweder mit onload oder async).
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
img.src = "bild.png";
img.onload = () => {
ctx.drawImage(img, 0, 0);
};
const loadImage = src =>
new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(img);
img.src = src;
});
const img = await loadImage("bild.png");
ctx.drawImage(img, 0, 0);
document.getElementById("upload").addEventListener("change", (e) => {
const img = new Image();
img.onload = () => {
originalImage = img;
canvas.width = img.width;
canvas.height = img.height;
draw();
};
img.src = URL.createObjectURL(e.target.files[0]);
});
Bild zeichnen und ein Crop-Rechteck einsetzen, das vergrößert, verkleinert und verschoben werden kann.
function draw() {
if (!originalImage) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImage, 0, 0);
// Overlay abdunkeln
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Crop-Bereich freischneiden
ctx.clearRect(cropRect.x, cropRect.y, cropRect.w, cropRect.h);
ctx.drawImage(
originalImage,
cropRect.x, cropRect.y, cropRect.w, cropRect.h,
cropRect.x, cropRect.y, cropRect.w, cropRect.h
);
// Rahmen
ctx.strokeStyle = "red";
ctx.lineWidth = 2;
ctx.strokeRect(cropRect.x, cropRect.y, cropRect.w, cropRect.h);
// Resize-Handle (unten rechts)
ctx.fillStyle = "white";
ctx.fillRect(
cropRect.x + cropRect.w - 10,
cropRect.y + cropRect.h - 10,
10,
10
);
}
SVG als Bild in canvas
In einen Canvas können nicht nur Bitmaps (JPG und PNG), sondern auch SVG-Grafiken direkt geladen werden.
Das Verfahren beim Laden eines SVG in ein canvas-Element ist dabei nicht anders als bei Bitmap-Bildern. Die Grafik wird in allen modernen Browsern angezeigt, auch in IE11 und Microsoft Edge.
let img = new Image();
img.addEventListener ("load", function () {
ctx.drawImage(img, 0, 0);
});
img.src = "wave-abs.svg";
Canvas drawImage: scale und clip
Mit zwei weiteren Argumenten bestimmt der Canvas Breite und Höhe des Bildes, um das Original zu vergrößern oder zu verkleinern.
Die nächsten beiden Argumente verschieben den Ausschnitt im Canvas.
Einen vergrößerten oder verkleinerten Ausschnitt des Bildes zeigt HTML Canvas mit insgesamt 8 Argumenten:
- sx, sy
- Linke obere Ecke des Bildausschnitts
- sw, sh
- Breite und Höhe des Ausschnitts aus dem Originalbild
- dx, dy
- Linke obere Ecke des Bildausschnitts im Canvas
- dw, dh
- Breite und Höhe des Ausschnitts im Canvas
// ctx.drawImage ( image, sx, sy, sw, sh, dx, dy, dw, dh ); h = canvas.height; w = canvas.width; // Vergrößern um Faktor 2 ctx.clearRect ( 0 , 0 , w , h ); ctx.drawImage (image, 100, 100, w/2, h/2, 0, 0, w, h ); // Verkleinern um Faktor 2 ctx.clearRect ( 0 , 0 , w , h ); ctx.drawImage ( image, 0, 0, w, h, 100, 100, w/2, h/2 ); // Reset ctx.clearRect ( 0 , 0 , w , h ); ctx.drawImage ( image, 0, 0, w, h );
Verkleinern und Vergrößern arbeitet ohne Weiteres auf den Pixeln des Originalbildes, auch wenn die Pixel durch das Script verändert wurden.
Werden die Pixel geändert, reicht ein einfaches Reset des Canvas nicht aus: Um wieder an die Pixel des Originalbildes zu kommen, müssen die Pixel am Anfang des Scripts in einem Puffer gespeichert werden und beim Reset muss der Puffer die Original-Pixel wieder herstellen. Das einfache Reset wie im Codebeispiel reicht nicht aus.
Javascript Canvas Sicherheit
Aus Sicherheitsgründen darf Javascript nur Bilder innerhalb der selben Domaine / desselben Verzeichnisses manipulieren. Die Pixel eines Bildes auf einem Remote Server können also nicht analysiert und manipuliert werden (eigentlich schade … ).
IE analysiert und manipuliert sogar nur Bilder innerhalb einer Server-Umgebung, nicht aber im lokalen Verzeichnis des Rechners.
Javascript Zugriff auf Canvas-Pixel
Um die Pixel eines Bildes zu manipulieren – z.B. aufzuhellen oder zu invertieren – müssen die Pixel linearisiert werden. Aus ist es mit der schönen Pixelmatrix eines Bildes: Wir erreichen einen Pixel nicht über »gibt mir Pixel 10 in Reihe 3«. Alle Pixel liegen in einem einfachen eindimensionalen Array.
let imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); let data = imageData.data;
gibt ein CanvasPixelArray mit den Farbwerten der Pixel des Bildausschnitts zurück.
Das heißt aber immer noch nicht, dass der Pixel 10 in Reihe 3 jetzt auf dem Index 3*10 - 1 liegt, sondern nur, dass der Pixel auf dieser Position beginnt.
RGB und Alpha: Take 4
Javascript Canvas stellt alle Pixel des Bildes durch vier Werte dar: Rot, Grün, Blau und Alpha.
Der Index des ersten Pixels ist 0, der Index des zweiten Pixels ist 4, der Index des dritten Pixels ist 12, …. Jedes Pixel beginnt also auf einem Vielfachen von 4.
Der mittlere Pixel einer 3x3 Matrix beginnt auf Index 16 und enthält die RGB-Werte auf 16+1 (Grün), 16+2 (Blau), 16+3 (Alpha)
let red = ((y-1) * (width * 4)) + ((x - 1) * 4); let blue = ((y-1) * (width * 4)) + ((x - 1) * 4) + 1; let green = ((y-1) * (width * 4)) + ((x - 1) * 4) + 2; let alpha = ((y-1) * (width * 4)) + ((x - 1) * 4) + 3;
$('#alpha').click(function () {
for (let x=0; x<numPixels; x++) {
if (parseInt(pixels[x*4+3]) == 0 ) {
pixels[x*4] = 0;
pixels[x*4+1] = 0;
pixels[x*4+2] = 0;
pixels[x*4+3] = 255;
}
}
ctx.clearRect ( 0 , 0 , canvasWidth , canvasHeight );
ctx.putImageData(imageData, 0, 0);