用JavaScript从RGB像素阵列向屏幕绘制图像的最快方法是什么?

时间:2019-12-30 22:23:34

标签: javascript canvas

我正在研究图形前端,该前端通过将压缩图像发送到客户端(想想VNC)来呈现服务器端并将屏幕更新推送到浏览器。我已经决定对PNG进行编码的开销过高,因此目前我正在通过网络套接字(启用压缩)发送8位RGB像素值的原始Blob。这实际上非常快,我看到了很大的压缩增益(例如75K-> 2.7k)。

但是,在客户端上,我必须获取原始像素,然后将其绘制到画布上。这是我目前最好的代码性能:

// receive message from server
self.ws.onmessage = function (evt) {
    // get image offset
    var dv = new DataView(evt.data);
    var dx = dv.getInt16(0);
    var dy = dv.getInt16(2);
    var ww = dv.getInt16(4);
    var hh = dv.getInt16(6);
    var offset = 8;

    // get context to canvas and create image
    var ctx = self.canvas.getContext("2d");
    var img = ctx.createImageData(ww, hh);

    // unpack image data
    var start = performance.now();
    var dst = 0, src = offset;
    for (var ii=0; ii < ww*hh; ii++) {
        img.data[dst++] = dv.getUint8(src++);
        img.data[dst++] = dv.getUint8(src++);
        img.data[dst++] = dv.getUint8(src++);
        img.data[dst++] = 255;
    }

    // draw image
    ctx.putImageData(img, dx, dy, 0, 0, ww, hh);
    var end = performance.now();

    console.log("draw took " + (end-start) + " milliseconds");

上述75K图像(1000x500像素)以这种方式渲染需要约53ms的时间,这是很长的时间。进行绘制操作的最快方法是什么?我可以输出RGBA像素,而不是那样,使生活更轻松。

编辑:似乎这可能更多是Firefox的问题,Chrome浏览器平均运行该解码循环的时间约为2.5ms。

编辑第二个:

切换到完整的RGBA输出(由于压缩,不会增加太多开销),并使用以下代码直接包装websocket缓冲区要快得多:

// receive message from server
self.ws.onmessage = function (evt) {
    // get image offset
    var dv = new DataView(evt.data);
    var dx = dv.getInt16(0);
    var dy = dv.getInt16(2);
    var ww = dv.getInt16(4);
    var hh = dv.getInt16(6);

    // get context to canvas and create image
    var ctx = self.canvas.getContext("2d");

    // draw image data
    var start = performance.now();
    ctx.putImageData(
        new ImageData(new Uint8ClampedArray(evt.data, 8), ww, hh),
        dx, dy,
        0,  0,
        ww, hh
    );
    var end = performance.now();

    console.log("draw took " + (end-start) + " milliseconds");
}

现在,在Firefox中渲染大图像需要大约1ms的时间,而在Chrome中则需要350us。足够好了。

1 个答案:

答案 0 :(得分:1)

切换到完整的RGBA输出(由于压缩,不会增加太多开销),并使用以下代码直接包装websocket缓冲区要快得多:

describe('test', () => {
  it('test', () => {

    // page one. Register 1 beforeunload event, and prevent the unload event.
    // -------------------------------------------------------------------------

    cy.visit('/a');

    cy.window().then( window => {
      window.addEventListener('beforeunload', () => {
        return 'one';
      });
    });

    // redirect to page two. Assert a prevented unload event.
    // Register another, but don't prevent unload.
    // -------------------------------------------------------------------------

    cy.visit('/b');

    cy.assertBeforeUnload( ret => {
      expect(ret).to.eq('one');
    });

    cy.window().then( window => {
      // register, but don't prevent
      window.onbeforeunload = () => {};
    });

    // page three. Assert a non-prevented unload event. Register none.
    // -------------------------------------------------------------------------

    cy.visit('/c');

    cy.assertBeforeUnload( ret => {
      // assert an event fired, but returned nothing (indicated by `null`)
      expect(ret).to.eq(null);
    });

    // page four. Assert no beforeunload event was fired.
    // -------------------------------------------------------------------------

    cy.visit('/d');

    cy.assertBeforeUnload( ret => {
      expect(ret).to.eq(undefined);
    });
  });
});

现在,在Firefox中渲染大图像需要大约1ms的时间,而在Chrome中则需要350us。足够好了。