调整画布图像大小而不模糊它

21

我有一张小图片,我想要在画布上渲染它,就像这样:

ctx.drawImage(img, 0, 0, img.width*2, img.height*2);

我希望能展示一个锐利的放大图像(每个像素点有4个相同像素)。然而,这段代码(在Mac上的Chrome 29版本)会使图像模糊。以Photoshop的术语来说,看起来它使用了“双三次”重采样算法,而不是“最近邻插值”算法。

在某些情况下很有用(比如复古游戏),是否有可能产生一个锐利的放大图像,或者我需要在服务器上为每个图像大小准备单独的图像文件?


最佳方法是为每个尺寸拥有单独的图像(速度快、质量较好),另一种方法是只拥有最大尺寸的图像,然后在需要时调整大小(存储占用小、质量较好)。 - slash197
如果我缩小尺寸,它仍然模糊。 - JJJollyjim
1
它不应该模糊,你保持了宽高比吗? - slash197
@slash197 我犯了一个错误。经过进一步测试,它实际上可以正常缩小。不过这种方式似乎浪费了很多带宽,我仍然希望有一个解决方案。 - JJJollyjim
@slash197 那个问题和我的相反。他想要双三次插值,但却得到了最近邻插值,而我则想要最近邻插值。 - JJJollyjim
显示剩余4条评论
3个回答

36

简单地关闭画布对图像的抗锯齿 - 不幸的是这个属性仍然有供应商前缀,因此有以下变化:

context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;

然后绘制图像。

对于不支持此功能的旧版本和浏览器,您可以使用 CSS 代替:

canvas {
    image-rendering: optimizeSpeed;             // Older versions of FF
    image-rendering: -moz-crisp-edges;          // FF 6.0+
    image-rendering: -webkit-optimize-contrast; // Webkit (non standard naming)
    image-rendering: -o-crisp-edges;            // OS X & Windows Opera (12.02+)
    image-rendering: crisp-edges;               // Possible future browsers.
    -ms-interpolation-mode: nearest-neighbor;   // IE (non standard naming)
}

点击此处进行在线测试


1
太棒了,这比缩小大文件或使用getImageData的解决方案要好得多(而且更优雅)。 - JJJollyjim
2
未加前缀的 context.imageSmoothingEnabled 至少在 Chrome 33 版本中可用。 - Luke

4

请查看此示例:http://jsfiddle.net/yPFjg/

它将图像加载到画布中,然后创建一个调整大小的副本并将其用作精灵。

通过一些修改,您可以实现一个能够动态调整图片尺寸的图像加载器。

var ctx = document.getElementById('canvas1').getContext('2d');
var img = new Image();
var original = document.createElement("canvas");
var scaled = document.createElement("canvas");

img.onload = function() {
    var oc = original.getContext('2d');
    var sc = scaled.getContext('2d');
    oc.canvas.width = oc.canvas.height = 16;
    sc.canvas.width = sc.canvas.height = 32;
    oc.drawImage(this, 0, 0);    
    var od = oc.getImageData(0,0,16,16);
    var sd = sc.getImageData(0,0,32,32);
    for (var x=0; x<32; x++) {
        for (var y=0; y<32; y++) {
            for (var c=0; c<4; c++) {        
                // you can improve these calculations, I let them so for clarity        
                sd.data[(y*32+x)*4+c] = od.data[((y>>1)*16+(x>>1))*4+c];
            }
        }
    }
    sc.putImageData(sd, 0, 0);
    ctx.drawImage(scaled, 0, 0);    
}

img.src = document.getElementById('sprite').src;

关于getImageData的一些注意事项:它返回一个带有数组的对象。该数组的大小为height*width*4。颜色分量以RGBA顺序(红、绿、蓝、alpha,每个值占8位)存储。


这在Firefox中对我不起作用(JS控制台中没有错误)。 http://i.imgur.com/DDqlxTS.png - JJJollyjim
已修复火狐浏览器的问题。我已更改图像创建(第2行)。 - Pedro L.

0
要调整图像的大小并保持锐利/像素化/非平滑质量,一种调整大小的方法是使用createImageBitmap和resizeQuality选项。
let resizeWidth = imageSource.width * 2;
let resizeHeight = imageSource.height * 2;
let resizeQuality = 'pixelated';
let bitmap = await createImageBitmap(imageSource, {resizeWidth, resizeHeight, resizeQuality});

然后在新的画布上绘制位图。
另一种调整大小的方法是使用drawImage与imageSmoothingEnabled和imageSmoothingQuality选项。
let canvas = Object.assign(document.createElement('canvas'), {width: resizeWidth, height: resizeHeight});
let context = canvas.getContext('2d', {colorSpace: 'srgb', pixelFormat: 'unorm8'});
if (resizeQuality === 'pixelated') {
  context.imageSmoothingEnabled = false;
}else{
  context.imageSmoothingEnabled = true;
  context.imageSmoothingQuality = resizeQuality;
}
if (bitmap.width === resizeWidth) { // check if the resizeWith/resizeHeight option is supported
  context.drawImage(bitmap, 0, 0); // draw resized bitmap without resizing
}else{
  context.drawImage(bitmap, 0, 0, resizeWidth, resizeHeight); // draw bitmap with resizing
}
bitmap.close();

查看 CodePen 演示 图像像素化/非平滑调整大小

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接