Tôi đã giải quyết vấn đề này vài năm trước và tải lên giải pháp của mình cho github là https://github.com/rossturner/HTML5-ImageUploader
Câu trả lời của robertc sử dụng giải pháp được đề xuất trong bài đăng trên blog của Mozilla Hacks , tuy nhiên tôi thấy điều này cho chất lượng hình ảnh thực sự kém khi thay đổi kích thước theo tỷ lệ không phải là 2: 1 (hoặc bội số của nó). Tôi bắt đầu thử nghiệm các thuật toán thay đổi kích thước hình ảnh khác nhau, mặc dù hầu hết kết thúc khá chậm hoặc chất lượng khác cũng không tốt.
Cuối cùng tôi đã đưa ra một giải pháp mà tôi tin rằng thực thi nhanh chóng và cũng có hiệu suất khá tốt - vì giải pháp Mozilla sao chép từ 1 canvas sang một công việc khác nhanh chóng và không làm giảm chất lượng hình ảnh theo tỷ lệ 2: 1, đưa ra mục tiêu là x pixel rộng và y pixel cao, tôi sử dụng phương pháp thay đổi kích thước canvas này cho đến khi hình ảnh nằm giữa x và 2 x , và y và 2 y . Lúc này tôi chuyển sang thay đổi kích thước hình ảnh thuật toán cho "bước" cuối cùng là thay đổi kích thước xuống kích thước đích. Sau khi thử một số thuật toán khác nhau, tôi đã giải quyết được phép nội suy song tuyến tính được lấy từ một blog không còn trực tuyến nữa nhưng có thể truy cập qua Lưu trữ Internet, cho kết quả tốt, đây là mã áp dụng:
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;
}
}
};
Điều này chia tỷ lệ hình ảnh xuống chiều rộng config.maxWidth
, duy trì tỷ lệ khung hình gốc. Tại thời điểm phát triển, nó hoạt động trên iPad / iPhone Safari ngoài các trình duyệt máy tính để bàn lớn (IE9 +, Firefox, Chrome), vì vậy tôi hy vọng nó vẫn sẽ tương thích với sự hấp thụ rộng rãi hơn của HTML5 ngày hôm nay. Lưu ý rằng lệnh gọi canvas.toDataURL () có loại mime và chất lượng hình ảnh sẽ cho phép bạn kiểm soát chất lượng và định dạng tệp đầu ra (có khả năng khác với đầu vào nếu bạn muốn).
Điểm duy nhất không bao gồm là duy trì thông tin định hướng, không có kiến thức về siêu dữ liệu này, hình ảnh được thay đổi kích thước và lưu nguyên trạng, làm mất bất kỳ siêu dữ liệu nào trong hình ảnh cho định hướng có nghĩa là hình ảnh được chụp trên thiết bị máy tính bảng "lộn ngược" được hiển thị như vậy, mặc dù chúng sẽ được lật trong kính ngắm camera của thiết bị. Nếu đây là một mối quan tâm, bài đăng trên blog này có một hướng dẫn và ví dụ mã tốt về cách thực hiện điều này, mà tôi chắc chắn có thể được tích hợp vào mã trên.