42

首先:我想做什么?

我有一个查看图像的应用程序。它使用 canvas 元素来渲染图像。你可以放大,你可以缩小,你可以拖动它。这部分现在完美运行。

但是,假设我有一张带有很多文字的图像。它的分辨率为 1200x1700,我的画布为 1200x900。最初,当缩小时,这会导致渲染分辨率约为 560x800。

我的实际绘图是这样的:

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

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

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

好吧,我在互联网的每个角落连续搜索了 4-5 个小时,但没有找到我需要的东西。我发现了“imageSmoothingEnabled”选项、不能在画布上使用的“image-rendering”CSS 样式、在浮动位置渲染以及插值算法的许多 JavaScript 实现(这些对于我的目的来说太慢了)

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

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

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

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

1 回答 1

59

你需要“下台”几次。您需要将其重新缩放到中间尺寸,而不是从非常大的图像缩放到非常小的图像。

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

var w = 1280;
var h = 853;

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

或者您可以将其以 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);

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

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

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

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>

在 jsfiddle 上查看相同的片段。

于 2013-09-12T13:41:26.583 回答