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

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

问题描述

首先:我想做什么?



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



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



我的实际绘图看起来像这样:

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

这张图片上的小文字看起来真的很糟糕,特别是当与其他图片浏览者),或者甚至html< img>元素。



我发现浏览器插值算法是这个问题的原因。比较不同的浏览器显示,Chrome呈现缩放的图片是最好的,但仍然不够好。



我在Interwebs的每个角落搜索了4-5个小时,没有找到我需要的。我发现了imageSmoothingEnabled选项,图像渲染CSS样式,您不能在画布上使用,在浮点位置渲染和许多JavaScript实现插值算法(这些)会降低我的目的)。



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



所以:有没有任何好的和快速的方法来有更好的插值?我当前的想法是创建一个图像对象,调整大小这(因为img有良好的插值,当缩放!)然后渲染。不幸的是,应用img.width似乎只影响显示的宽度...



非常感谢您的答案! :)



更新:感谢Simon,我可以解决我的问题。
这里是我使用的动态缩放算法。请注意,它保持高宽比,高度参数仅用于避免更多的浮点计算。它现在只是缩小。

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


解决方案

几次。



考虑一个图像,你想要绘制一个1/6的图像,而不是从一个非常大的图像缩放到一个非常小的,你需要重新缩放到中间尺寸。规模。您可以这样做:

  var w = 1280; 
var h = 853;

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

或者你可以将它绘制到一个内存中的画布1/2,再次缩放1/2刻度。结果是1/6缩放图像,但我们使用三个步骤:

  var can2 = document.createElement ); 
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); 

可以看到实时差异,这里是:



  canvas {border:1px solid gray;}  

 < canvas id =canvas1width =400height =400>< / canvas>  



查看相同


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.

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

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.

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.

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 :))

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...

Many thanks in advance for your answers!! :)

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.

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

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>

View same snippet on jsfiddle.

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

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