HTML5 Canvas - 在屏幕上显示像素颜色数组的最快方式

Posted

技术标签:

【中文标题】HTML5 Canvas - 在屏幕上显示像素颜色数组的最快方式【英文标题】:HTML5 Canvas - Fastest way to display an array of pixel colors on the screen 【发布时间】:2014-12-28 19:55:40 【问题描述】:

给定一个 40x30 的颜色数组,在 400x300 画布的屏幕上显示它们的最快方法是什么?也就是说,每种颜色都需要画布元素中的 100 个像素。

使用 html5 中的 canvas 元素,我创建了 4 种可能的方法来做到这一点。 http://jsperf.com/pixel-plotting

随机像素数据被加载到 40x30 数组中。然后将其显示在大小为 400x300 的画布中,这样每个逻辑像素在屏幕上由 100 个像素表示。

最快的方法(使用 Chrome 38.0)是在隐藏的画布中创建图像数据,然后使用 toDataURL 将其复制到屏幕上显示的画布中。属性 imageSmoothingEnabled 设置为 false 以阻止像素在放大时模糊。

还有其他方法吗?我尝试使用较小的画布尺寸并在 CSS 中将其放大,但这会导致像素模糊。

我想知道测试在其他网络浏览器中的表现;尤其是手机。

将像素数据绘制到屏幕上非常重要,因为它需要每秒至少进行 30 次,因此即使是很小的调整也可以大大提高效率。

这是我测试的四种方法的代码。

方法一

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) 
var x = (i/4)%40;
    var y = Math.floor(i/160);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;

ctx1.putImageData(imgData, 0, 0);

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

方法二

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

var imgData = ctx.createImageData(400, 300);
for (var i=0; i<imgData.data.length; i+=4) 
    var x = Math.floor(((i/4)%400)/10);
    var y = Math.floor(i/16000);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;

ctx.putImageData(imgData, 0, 0);

方法三

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

for (var x = 0; x < 40; x++) 
    for (var y = 0; y < 30; y++) 
        ctx1.fillStyle = pixelData2[x][y];
        ctx1.fillRect(x, y, 1, 1);
    


png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

方法四

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

for (var x=0; x<40; x++) 
    for (var y=0; y<30; y++) 
        ctx.fillStyle = pixelData2[x][y];
        ctx.fillRect(x*10, y*10, 10, 10);
    

【问题讨论】:

由于没有透明度,像素不会自己覆盖,而您只能完全看到最后一个矩形吗?您能否提供您正在尝试执行的操作的示例图片? 是的,他们会的。这是一个很笼统的问题。链接:jsperf.com/pixel-plotting 显示一些随机像素数据(您必须单击“运行测试”)它将多次运行代码以查看 4 种方法中哪一种最快。生成的动画是由于测试方法造成的,与问题无关。我想我是在问是否有第五种方法比我提出的四种方法中的任何一种都快。 是的,是的,看到了测试。我想说,第五种方式可能只是针对特定场景的一些巧妙优化(如我提到的不透明像素的情况,渲染 95% 的像素毫无意义。)。一般来说,我认为你已经涵盖了几乎所有可能的方法,但我们会看看是否有人想出了新的东西。 顺便说一句。当您可以直接将画布用作图像源时,使用图像元素和 toDataURL 毫无意义。只需绘制:ctx2.drawImage(c1, 0, 0, 400, 300);(参考 m3)。 哇,这让它快了很多。看起来最快的方法是使用 2 个画布和 putImageData。 【参考方案1】:

感谢 cmets 的回复。 根据 Ken Frystenberg 的评论,我更新了 jsperf 上的测试:http://jsperf.com/pixel-plotting/5

似乎最快的方法是编辑屏幕上不可见的画布的图像数据。然后,在第二个画布的上下文中,将属性 imageSmoothingEnabled 设置为 false。最后调用 drawImage 将第一个画布作为图像的来源。

为了在大多数浏览器中支持此方法,适当的前缀被添加到画布上下文的 imageSmoothingEnabled 属性中。

这是最终代码:

var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) 
    var x = (i/4)%40; 
    var y = Math.floor(i/160); 
    imgData.data[i] = pixelData1[x][y].r; 
    imgData.data[i+1] = pixelData1[x][y].g; 
    imgData.data[i+2] = pixelData1[x][y].b; 
    imgData.data[i+3] = 255; 

ctx1.putImageData(imgData, 0, 0);

c2.width = 400;
c2.height = 300;

ctx2.mozImageSmoothingEnabled = false;
ctx2.webkitImageSmoothingEnabled = false;
ctx2.msImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(c1, 0, 0, 400, 300);

【讨论】:

以上是关于HTML5 Canvas - 在屏幕上显示像素颜色数组的最快方式的主要内容,如果未能解决你的问题,请参考以下文章

“canvas是基于像素”的含义是什么,SVG是基于矢量的

html5画布:按颜色剪裁

如何在 HTML5 画布上逐个像素地绘制

Android Canvas:仅在透明背景上绘制圆圈

HTML5 Canvas 图像缩放问题

Android课程---手机尺寸相关的概念 +尺寸单位+关于颜色