如何在逐个像素的HTML5画布上绘图

时间:2013-12-30 01:57:23

标签: javascript html5 performance canvas html5-canvas

假设我有一个900x900的HTML5 Canvas元素。

我有一个名为computeRow的函数,它接受网格上一行的数字作为参数,并返回一个包含900个数字的数组。每个数字代表0到200之间的数字。例如,有一个名为colors的数组,其中包含一个字符串数组,如rgb(0,20,20)

基本上,我所说的是我有一个函数可以逐个像素地说明画布上给定行中每个像素应该是什么颜色。多次运行此功能,我可以为画布上的每个像素计算颜色。

运行computeRow 900次的过程大约需要0.5秒。

然而,图像的绘制需要更长的时间。

我所做的是我编写了一个名为drawRow的函数,它将一个包含900个数字的数组作为输入并在画布上绘制它们。 drawRow运行的时间比computeRow长很多!我该如何解决这个问题?

drawRow很简单。它看起来像这样:

function drawRow(rowNumber, result /* array */) {
    var plot, context, columnNumber, color;
    plot = document.getElementById('plot');
    context = plot.getContext('2d');
    // Iterate over the results for each column in the row, coloring a single pixel on
    // the canvas the correct color for each one.
    for(columnNumber = 0; columnNumber < width; columnNumber++) {
        color = colors[result[columnNumber]];
        context.fillStyle = color;
        context.fillRect(columnNumber, rowNumber, 1, 1);
    }
}

3 个答案:

答案 0 :(得分:8)

我不确定你到底想要做什么,所以如果我错了,我道歉。

如果您尝试为画布上的每个像素写一种颜色,您可以这样做:

var ctx = document.getElementById('plot').getContext('2d');
var imgdata = ctx.getImageData(0,0, 640, 480);
var imgdatalen = imgdata.data.length;
for(var i=0;i<imgdatalen/4;i++){  //iterate over every pixel in the canvas
  imgdata.data[4*i] = 255;    // RED (0-255)
  imgdata.data[4*i+1] = 0;    // GREEN (0-255)
  imgdata.data[4*i+2] = 0;    // BLUE (0-255)
  imgdata.data[4*i+3] = 255;  // APLHA (0-255)
}
ctx.putImageData(imgdata,0,0);

这比为每个像素绘制矩形要快得多。您唯一需要做的就是将颜色分成rgba()值。

答案 1 :(得分:4)

fillRect仅用于 - 填充具有单一颜色的区域,而不是逐个像素。如果你逐个像素地进行,那么当你受CPU限制时,它必然会变慢。在这些情况下,您可以通过观察CPU负载来检查它。如果

,代码将变得更高效
  • 创建一个单独的图像,并填入所需的图像数据。您可以使用工作线程在后台填充此图像。有关使用工作线程的示例,请参阅http://gpupowered.org/node/11

  • 上的博客文章
  • 然后,使用context.drawImage(image, dx, dy)将图像blit到您想要的第二个上下文中。

答案 2 :(得分:3)

如果你从每个像素的数组中读取颜色值作为字符串,那么你使用什么技术并不重要,因为瓶颈就是那个部分。

对于每个像素,成本分为(大致)这些步骤:

  • 查找数组(实际上是JavaScript中的节点/链接列表)
  • 获取字符串
  • 将字符串传递给fillStyle
    • 将字符串(内部)解析为颜色值
  • 准备绘制单个像素

这些是非常昂贵的操作性能。为了提高效率,您需要将该颜色数组转换为除了绘制操作之前具有字符串的数组之外的其他颜色。

您可以通过以下几种方式实现:

  • 如果阵列来自服务器,请尝试在发送之前将数组格式化为blob / typed数组。这样,您可以将返回数组的内容按原样复制到画布'像素缓冲区。
  • 使用Web worker解析数组并将其作为可传输对象传回,然后将它们复制到画布缓冲区中。这可以直接复制到画布上 - 或者反过来做,将像素缓冲区传递给worker,填充并返回。
  • 按颜色值对数组进行排序,并按颜色组更新颜色。这样,您可以使用fillStyle或将颜色计算为Uint32值,您可以使用Uint32缓冲区视图将其复制到画布。如果颜色非常分散,则效果不佳,但如果颜色代表一个小调色板,则效果正常。

如果您坚持使用颜色的格式,那么第二个选项是我建议的主要取决于尺寸。它使您的代码异步,因此这也是您需要处理的一个方面(即操作完成时的回调)。

你当然可以在相同的线程上解析数组,并找到一种方法为用户伪装一点,以防它产生明显的延迟(即使对于较慢的计算机,900x900也不应该是那么大的交易)。

如果转换数组,则将其转换为无符号32位值,并将结果存储在Typed数组中。这样您就可以使用Uint32迭代画布像素缓冲区,这比使用每字节字节方法快得多。