Eu uso elementos de tela html5 para redimensionar imagens no meu navegador. Acontece que a qualidade é muito baixa. Encontrei o seguinte: Desativar interpolação ao dimensionar um <canvas> mas isso não ajuda a aumentar a qualidade.
Abaixo está o meu código css e js, bem como a imagem scalled com Photoshop e redimensionada na API do canvas.
O que devo fazer para obter a melhor qualidade ao dimensionar uma imagem no navegador?
Nota: Quero reduzir uma imagem grande para uma pequena, modificar a cor em uma tela e enviar o resultado da tela para o servidor.
CSS:
canvas, img {
image-rendering: optimizeQuality;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
JS:
var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {
var originalContext = $originalCanvas[0].getContext('2d');
originalContext.imageSmoothingEnabled = false;
originalContext.webkitImageSmoothingEnabled = false;
originalContext.mozImageSmoothingEnabled = false;
originalContext.drawImage(this, 0, 0, 379, 500);
});
A imagem foi redimensionada com o photoshop:
A imagem redimensionada na tela:
Editar:
Tentei fazer o downscaling em mais de uma etapa, conforme proposto em:
Redimensionando uma imagem em uma tela HTML5 e Html5 drawImage: como aplicar antialiasing
Esta é a função que eu usei:
function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 2;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
console.log("Step: "+i);
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// copy back to canvas
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
}
Aqui está o resultado se eu usar um dimensionamento em 2 etapas:
Aqui está o resultado se eu usar um dimensionamento em três etapas:
Aqui está o resultado se eu usar um dimensionamento em 4 etapas:
Aqui está o resultado se eu usar um dimensionamento de 20 etapas:
Nota: Acontece que de 1 a 2 etapas há uma grande melhoria na qualidade da imagem, mas quanto mais etapas você adiciona ao processo, mais difusa fica a imagem.
Existe uma maneira de resolver o problema em que a imagem fica mais confusa quanto mais etapas você adicionar?
Edit 04/10/2013: Eu tentei o algoritmo do GameAlchemist. Aqui está o resultado comparado ao Photoshop.
Imagem do PhotoShop:
Algoritmo do GameAlchemist: