Saya mengatasi masalah ini beberapa tahun yang lalu dan mengunggah solusi saya ke github sebagai https://github.com/rossturner/HTML5-ImageUploader
jawaban robertc menggunakan solusi yang diusulkan dalam posting blog Mozilla Hacks , namun saya menemukan ini memberikan kualitas gambar yang sangat buruk ketika mengubah ukuran ke skala yang bukan 2: 1 (atau beberapa darinya). Saya mulai bereksperimen dengan algoritme pengubahan ukuran gambar yang berbeda, meskipun sebagian besar pada akhirnya cukup lambat atau kualitasnya tidak terlalu bagus.
Akhirnya saya datang dengan solusi yang saya percaya dijalankan dengan cepat dan memiliki kinerja yang sangat bagus juga - sebagai solusi Mozilla menyalin dari 1 kanvas ke karya lain dengan cepat dan tanpa kehilangan kualitas gambar pada rasio 2: 1, diberikan target x piksel lebar dan tinggi piksel y , saya menggunakan metode pengubahan ukuran kanvas ini sampai gambar berada di antara x dan 2 x , dan y dan 2 y . Pada titik ini saya kemudian beralih ke pengubahan ukuran gambar algoritmik untuk "langkah" akhir pengubahan ukuran ke ukuran target. Setelah mencoba beberapa algoritma yang berbeda, saya memilih interpolasi bilinear yang diambil dari blog yang tidak online lagi tetapi dapat diakses melalui Internet Archive, yang memberikan hasil yang baik, inilah kode yang berlaku:
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;
}
}
};
Ini skala gambar ke lebar config.maxWidth
, mempertahankan rasio aspek asli. Pada saat pengembangan ini bekerja pada iPad / iPhone Safari selain browser desktop utama (IE9 +, Firefox, Chrome) jadi saya berharap ini akan tetap kompatibel mengingat penggunaan HTML5 yang lebih luas saat ini. Perhatikan bahwa panggilan canvas.toDataURL () menggunakan tipe mime dan kualitas gambar yang akan memungkinkan Anda untuk mengontrol kualitas dan format file output (berpotensi berbeda untuk input jika Anda mau).
Satu-satunya hal yang tidak dibahas adalah mempertahankan informasi orientasi, tanpa sepengetahuan metadata ini, gambar diubah ukurannya dan disimpan apa adanya, kehilangan metadata apa pun di dalam gambar untuk orientasi yang berarti bahwa gambar yang diambil pada perangkat tablet "terbalik" adalah ditampilkan seperti itu, meskipun mereka akan terbalik di jendela bidik kamera perangkat. Jika ini yang menjadi perhatian, posting blog ini memiliki panduan dan contoh kode yang baik tentang bagaimana mencapainya, yang saya yakin dapat diintegrasikan dengan kode di atas.