Ich habe dieses Problem vor einigen Jahren angegangen und meine Lösung als https://github.com/rossturner/HTML5-ImageUploader auf github hochgeladen
Die Antwort von robertc verwendet die im Blog-Beitrag von Mozilla Hacks vorgeschlagene Lösung. Ich fand jedoch, dass dies eine wirklich schlechte Bildqualität ergab, wenn die Größe auf einen Maßstab geändert wurde, der nicht 2: 1 (oder ein Vielfaches davon) war. Ich fing an, mit verschiedenen Algorithmen zur Größenänderung von Bildern zu experimentieren, obwohl die meisten ziemlich langsam waren oder auch keine gute Qualität hatten.
Schließlich habe ich eine Lösung gefunden, die meiner Meinung nach schnell ausgeführt werden kann und auch eine ziemlich gute Leistung aufweist - da die Mozilla-Lösung zum Kopieren von einer Leinwand auf eine andere schnell und ohne Verlust der Bildqualität im Verhältnis 2: 1 bei einem Ziel von x funktioniert Pixel breit und y Pixel hoch. Ich verwende diese Methode zur Größenänderung der Leinwand, bis das Bild zwischen x und 2 x und y und 2 y liegt . An dieser Stelle wende ich mich dann der algorithmischen Bildgröße für den letzten "Schritt" der Größenänderung auf die Zielgröße zu. Nachdem ich verschiedene Algorithmen ausprobiert hatte, entschied ich mich für eine bilineare Interpolation aus einem Blog, das nicht mehr online ist, aber über das Internetarchiv zugänglich istHier ist der zutreffende 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;
}
}
};
Dadurch wird ein Bild auf eine Breite von verkleinert config.maxWidth
, wobei das ursprüngliche Seitenverhältnis beibehalten wird. Zum Zeitpunkt der Entwicklung funktionierte dies auf iPad / iPhone Safari zusätzlich zu den wichtigsten Desktop-Browsern (IE9 +, Firefox, Chrome), sodass ich davon ausgehe, dass es angesichts der breiteren Verbreitung von HTML5 heute noch kompatibel sein wird. Beachten Sie, dass der Aufruf canvas.toDataURL () einen MIME-Typ und eine Bildqualität verwendet, mit denen Sie die Qualität und das Ausgabedateiformat steuern können (möglicherweise anders als die Eingabe, wenn Sie dies wünschen).
Der einzige Punkt, der hier nicht behandelt wird, ist die Beibehaltung der Orientierungsinformationen. Ohne Kenntnis dieser Metadaten wird die Größe des Bilds geändert und unverändert gespeichert. Dabei gehen alle Metadaten im Bild zur Orientierung verloren, was bedeutet, dass Bilder, die auf einem Tablet-Gerät "verkehrt herum" aufgenommen wurden als solche gerendert, obwohl sie im Kamerasucher des Geräts umgedreht worden wären. Wenn dies ein Problem darstellt, enthält dieser Blog-Beitrag eine gute Anleitung und Codebeispiele, wie dies erreicht werden kann. Ich bin sicher, dass dies in den obigen Code integriert werden könnte.