Html 如何使用更好的插值在 html5 画布上缩放图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18761404/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 13:19:32  来源:igfitidea点击:

How to scale images on a html5 canvas with better interpolation?

javascripthtmlcanvasscalinginterpolation

提问by Kumpu

First of all: what am I trying to do?

首先:我想做什么?

I have an application to view images. It uses the canvas element to render the image. You can zoom in, you can zoom out, and you can drag it around. This part works perfectly right now.

我有一个应用程序来查看图像。它使用 canvas 元素来渲染图像。你可以放大,你可以缩小,你可以拖动它。这部分现在工作得很好。

But let's say I have an image with a lot of text. It has a resolution of 1200x1700, and my canvas has 1200x900. Initially, when zoomed out, this leads to a rendered resolution of ~560x800.

但是假设我有一个包含大量文本的图像。它的分辨率为 1200x1700,我的画布为 1200x900。最初,当缩小时,这会导致渲染分辨率约为 560x800。

My actual drawing looks like this:

我的实际绘图是这样的:

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

Small text on this image looks really, really bad, especially when compared to other image viewers (e.g. IrfanView), or even the html < img > element.

此图像上的小文字看起来非常非常糟糕,尤其是与其他图像查看器(例如 IrfanView)甚至 html < img > 元素相比时。

I figured out that the browsers interpolation algorithm is the cause of this problem. Comparing different browsers showed that Chrome renders scaled images the best, but still not good enough.

我发现浏览器的插值算法是这个问题的原因。比较不同的浏览器表明,Chrome 渲染缩放的图像是最好的,但仍然不够好。

Well I searched in every corner of the Interwebs for 4-5 hours straight and did not find what I need. I found the "imageSmoothingEnabled" option, "image-rendering" CSS styles which you can not use on canvas, rendering at float positions and many JavaScript implementations of interpolation algorithms (those are farto slow for my purpose).

好吧,我在互联网的每个角落连续搜索了 4-5 个小时,但没有找到我需要的东西。我发现“imageSmoothingEnabled”选项,“图像渲染” CSS样式,你不能在画布上使用,在浮位置和插值算法很多JavaScript实现渲染(这些是远远慢于我的目的)。

You may ask why I am telling you all of this: to save you the time to give me answers I already know

你可能会问我为什么要告诉你所有这些:为了节省你给我答案的时间,我已经知道了

So: is there anygood and fast way to have better interpolation? My current idea is to create an image object, resize this (because img has good interpolation when scaled!) and render it then. Unfortunately, applying img.width seems only to affect the displayed width...

那么:有没有什么好的和快速的方法来获得更好的插值?我目前的想法是创建一个图像对象,调整它的大小(因为 img 在缩放时有很好的插值!)然后渲染它。不幸的是,应用 img.width 似乎只会影响显示的宽度......

Update: Thanks to Simon, I could solve my problem. Here is the dynamic scaling algorithm I used. Notice that it keeps the aspect ratio, the height parameter is only for avoiding more float computing. It only scales down right now.

更新:感谢西蒙,我可以解决我的问题。这是我使用的动态缩放算法。注意它保持纵横比,高度参数只是为了避免更多的浮点计算。它现在只会缩小。

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);
    }

回答by Simon Sarris

You need to "step down" several times. Instead of scaling from a very large image to a very small, you need to re-scale it to intermediary sizes.

您需要多次“下台”。不是从非常大的图像缩放到非常小的图像,您需要将其重新缩放到中等大小。

Consider an image you want to draw at 1/6 scale. You could do this:

考虑要以 1/6 比例绘制的图像。你可以这样做:

var w = 1280;
var h = 853;

ctx.drawImage(img, 0, 0, w/6, h/6);   

Or you could draw it to an in-memory canvas at 1/2 scale, then 1/2 scale again, then 1/2 scale again. The result is a 1/6 scale image, but we use three steps:

或者,您可以将它以 1/2 的比例绘制到内存画布上,然后再以 1/2 的比例绘制,然后再以 1/2 的比例绘制。结果是一个 1/6 比例的图像,但我们使用三个步骤:

var can2 = document.createElement('canvas');
can2.width = w/2;
can2.height = w/2;
var ctx2 = can2.getContext('2d');

ctx2.drawImage(img, 0, 0, w/2, h/2);
ctx2.drawImage(can2, 0, 0, w/2, h/2, 0, 0, w/4, h/4);
ctx2.drawImage(can2, 0, 0, w/4, h/4, 0, 0, w/6, h/6);

Then you can draw that back to your original context:

然后你可以把它画回你原来的上下文:

ctx.drawImage(can2, 0, 0, w/6, h/6, 0, 200, w/6, h/6);

You can see the difference live, here:

您可以在此处实时查看差异:

var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');

var img = new Image();
var w = 1280;
var h = 853;
img.onload = function() {
    // step it down only once to 1/6 size:
    ctx.drawImage(img, 0, 0, w/6, h/6);   
    
    // Step it down several times
    var can2 = document.createElement('canvas');
    can2.width = w/2;
    can2.height = w/2;
    var ctx2 = can2.getContext('2d');
    
    // Draw it at 1/2 size 3 times (step down three times)
    
    ctx2.drawImage(img, 0, 0, w/2, h/2);
    ctx2.drawImage(can2, 0, 0, w/2, h/2, 0, 0, w/4, h/4);
    ctx2.drawImage(can2, 0, 0, w/4, h/4, 0, 0, w/6, h/6);
    ctx.drawImage(can2, 0, 0, w/6, h/6, 0, 200, w/6, h/6);
}



img.src = 'http://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Equus_quagga_%28Namutoni%2C_2012%29.jpg/1280px-Equus_quagga_%28Namutoni%2C_2012%29.jpg'
canvas {
    border: 1px solid gray;
}
<canvas id="canvas1" width="400" height="400"></canvas>

View same snippet on jsfiddle.

在 jsfiddle 上查看相同的片段。