如何使用更好的插值在 html5 画布上缩放图像? [英] How to scale images on a html5 canvas with better interpolation?

查看:18
本文介绍了如何使用更好的插值在 html5 画布上缩放图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先:我想做什么?

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

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.

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

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.

我的实际绘图是这样的:

My actual drawing looks like this:

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

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

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.

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

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.

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

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 far to slow for my purpose).

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

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

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

So: is there any good 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...

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

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

推荐答案

你需要多次下台".您不需要从非常大的图像缩放到非常小的图像,而是需要将其重新缩放到中等大小.

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.

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

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

var w = 1280;
var h = 853;

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

或者您可以将它以 1/2 的比例绘制到内存画布上,然后再以 1/2 的比例绘制,然后再以 1/2 的比例绘制.结果是一个 1/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:

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>

在 jsfiddle 上查看相同的片段.

这篇关于如何使用更好的插值在 html5 画布上缩放图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆