Ich habe dieses Problem vor ein paar Jahren in Angriff genommen und meine Lösung auf github hochgeladen als https://github.com/rossturner/HTML5-ImageUploader
Die Antwort von robertc verwendet die Lösung, die in der Mozilla Hacks Blogbeitrag Ich stellte jedoch fest, dass die Bildqualität sehr schlecht war, wenn die Größe nicht im Maßstab 2:1 (oder einem Vielfachen davon) geändert wurde. Ich begann mit verschiedenen Algorithmen zur Größenänderung von Bildern zu experimentieren, obwohl die meisten am Ende ziemlich langsam waren oder auch keine gute Qualität boten.
Schließlich habe ich eine Lösung gefunden, die meiner Meinung nach schnell ausgeführt werden kann und auch eine ziemlich gute Leistung hat - da die Mozilla-Lösung des Kopierens von einer Leinwand auf eine andere bei einem Verhältnis von 2:1 schnell und ohne Verlust der Bildqualität funktioniert, wenn man ein Ziel von x Pixel breit und y Pixel hoch ist, verwende ich diese Methode zur Größenänderung der Leinwand, bis das Bild zwischen x und 2 x et y und 2 y . An diesem Punkt wende ich mich dann für den letzten "Schritt" der Größenanpassung an die Zielgröße einem Algorithmus zu. Nachdem ich verschiedene Algorithmen ausprobiert hatte, entschied ich mich für die bilineare Interpolation aus einem Blog, der nicht mehr online ist, aber zugänglich über das Internet-Archiv , die gute Ergebnisse liefert, hier der entsprechende Code:
ImageUploader.prototype.scaleImage = function(img, completionCallback) {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
while (canvas.width >= (2 * this.config.maxWidth)) {
canvas = this.getHalfScaleCanvas(canvas);
}
if (canvas.width > this.config.maxWidth) {
canvas = this.scaleCanvasWithAlgorithm(canvas);
}
var imageData = canvas.toDataURL('image/jpeg', this.config.quality);
this.performUpload(imageData, completionCallback);
};
ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) {
var scaledCanvas = document.createElement('canvas');
var scale = this.config.maxWidth / canvas.width;
scaledCanvas.width = canvas.width * scale;
scaledCanvas.height = canvas.height * scale;
var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height);
this.applyBilinearInterpolation(srcImgData, destImgData, scale);
scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0);
return scaledCanvas;
};
ImageUploader.prototype.getHalfScaleCanvas = function(canvas) {
var halfCanvas = document.createElement('canvas');
halfCanvas.width = canvas.width / 2;
halfCanvas.height = canvas.height / 2;
halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height);
return halfCanvas;
};
ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) {
function inner(f00, f10, f01, f11, x, y) {
var un_x = 1.0 - x;
var un_y = 1.0 - y;
return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
}
var i, j;
var iyv, iy0, iy1, ixv, ix0, ix1;
var idxD, idxS00, idxS10, idxS01, idxS11;
var dx, dy;
var r, g, b, a;
for (i = 0; i < destCanvasData.height; ++i) {
iyv = i / scale;
iy0 = Math.floor(iyv);
// Math.ceil can go over bounds
iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv));
for (j = 0; j < destCanvasData.width; ++j) {
ixv = j / scale;
ix0 = Math.floor(ixv);
// Math.ceil can go over bounds
ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv));
idxD = (j + destCanvasData.width * i) * 4;
// matrix to vector indices
idxS00 = (ix0 + srcCanvasData.width * iy0) * 4;
idxS10 = (ix1 + srcCanvasData.width * iy0) * 4;
idxS01 = (ix0 + srcCanvasData.width * iy1) * 4;
idxS11 = (ix1 + srcCanvasData.width * iy1) * 4;
// overall coordinates to unit square
dx = ixv - ix0;
dy = iyv - iy0;
// I let the r, g, b, a on purpose for debugging
r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy);
destCanvasData.data[idxD] = r;
g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy);
destCanvasData.data[idxD + 1] = g;
b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy);
destCanvasData.data[idxD + 2] = b;
a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy);
destCanvasData.data[idxD + 3] = a;
}
}
};
Damit wird ein Bild auf eine Breite von config.maxWidth
unter Beibehaltung des ursprünglichen Seitenverhältnisses. Zum Zeitpunkt der Entwicklung funktionierte dies auf iPad/iPhone Safari zusätzlich zu den wichtigsten Desktop-Browsern (IE9+, Firefox, Chrome), so dass ich davon ausgehe, dass es angesichts der breiteren Akzeptanz von HTML5 heute immer noch kompatibel sein wird. Beachten Sie, dass der Aufruf canvas.toDataURL() einen Mime-Typ und eine Bildqualität entgegennimmt, die es Ihnen ermöglichen, die Qualität und das Format der Ausgabedatei zu steuern (möglicherweise anders als die Eingabe, wenn Sie es wünschen).
Der einzige Punkt, den dies nicht abdeckt, ist die Beibehaltung der Ausrichtungsinformationen. Ohne Kenntnis dieser Metadaten wird das Bild in der Größe verändert und so gespeichert, wie es ist, wobei alle Metadaten innerhalb des Bildes für die Ausrichtung verloren gehen, was bedeutet, dass Bilder, die auf einem Tablet-Gerät "auf dem Kopf stehend" aufgenommen wurden, auch so wiedergegeben werden, obwohl sie im Kamerasucher des Geräts gespiegelt worden wären. Wenn dies ein Problem ist, dieser Blogbeitrag hat einen guten Leitfaden und Code-Beispiele, wie dies zu erreichen, die ich bin sicher, könnte auf den obigen Code integriert werden.