¿Cómo escalar imágenes en un lienzo html5 con una mejor interpolación?

En primer lugar: ¿qué estoy tratando de hacer?

Tengo una aplicación para ver imágenes. Utiliza el elemento lienzo para renderizar la imagen. Puede acercar, puede alejar y puede arrastrarlo. Esta parte funciona perfectamente ahora mismo.

Pero digamos que tengo una imagen con mucho texto. Tiene una resolución de 1200x1700, y mi lienzo tiene 1200x900. Inicialmente, cuando se aleja, esto lleva a una resolución renderizada de ~ 560x800.

Mi dibujo real se ve así:

drawImage(src, srcOffsetX, srcOffsetY, sourceViewWidth, sourceViewHeight,
destOffsetX, destOffsetY, destWidth, destHeight);

El texto pequeño en esta imagen se ve realmente muy mal, especialmente cuando se compara con otros visores de imágenes (por ejemplo, IrfanView), o incluso el elemento html <img>.

Me di cuenta de que el algoritmo de interpolación de los navegadores es la causa de este problema. La comparación de diferentes navegadores mostró que Chrome hace que las imágenes a escala sean las mejores, pero no lo suficientemente buenas.

Bueno, busqué en todos los rincones de Interwebs durante 4-5 horas seguidas y no encontré lo que necesitaba. Encontré la opción "imageSmoothingEnabled", los estilos CSS de "representación de imagen" que no se pueden usar en el lienzo, la representación en posiciones flotantes y muchas implementaciones de JavaScript de algoritmos de interpolación (sonlejos ralentizar para mi propósito).

Puede preguntar por qué le estoy diciendo todo esto: para ahorrarle tiempo para darme respuestas que ya conozco

Entonces: está ahíalguna ¿Buena y rápida forma de tener una mejor interpolación? Mi idea actual es crear un objeto de imagen, cambiar el tamaño de este (porque img tiene una buena interpolación cuando se escala) y luego procesarlo. Desafortunadamente, aplicar img.width solo afecta el ancho mostrado ...

Actualizar: Gracias a Simon, pude resolver mi problema. Aquí está el algoritmo de escalado dinámico que utilicé. Observe que mantiene la relación de aspecto, el parámetro de altura es solo para evitar más computación flotante. Sólo se reduce ahora.

scale(destWidth, destHeight){
        var start = new Date().getTime();
        var scalingSteps = 0;
        var ctx = this._sourceImageCanvasContext;
        var curWidth = this._sourceImageWidth;
        var curHeight = this._sourceImageHeight;

        var lastWidth = this._sourceImageWidth;
        var lastHeight = this._sourceImageHeight;

        var end = false;
        var scale=0.75;
        while(end==false){
            scalingSteps +=1;
            curWidth *= scale;
            curHeight *= scale;
            if(curWidth < destWidth){
                curWidth = destWidth;
                curHeight = destHeight;
                end=true;
            }
            ctx.drawImage(this._sourceImageCanvas, 0, 0, Math.round(lastWidth), Math.round(lastHeight), 0, 0, Math.round(curWidth), Math.round(curHeight));
            lastWidth = curWidth;
            lastHeight = curHeight;
        }
        var endTime =new Date().getTime();
        console.log("execution time: "+ ( endTime - start) + "ms. scale per frame: "+scale+ " scaling step count: "+scalingSteps);
    }

Respuestas a la pregunta(1)

Su respuesta a la pregunta