Canvas Pixel Manipulation

JavaScript performance comparison

Revision 168 of this test case created by Chadnaut

Preparation code

<canvas id="canvas" height="256" width="256"></canvas>
      
<script>
Benchmark.prototype.setup = function() {
  //precompute everything to only test raw pixel pushing performance
  var canvas = document.getElementById('canvas'),
    canvasWidth = canvas.width,
    canvasHeight = canvas.height,
    ctx = canvas.getContext('2d'),
  
    imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
    data = imageData.data,
  
    buf = new ArrayBuffer(data.length),
    buf8 = new Uint8ClampedArray(buf),
    data32 = new Uint32Array(buf),
  
    pixelsI32 = new Int32Array(data.buffer),
    pixelsU32 = new Uint32Array(data.buffer),
    view = new DataView(data.buffer),
  
    temp = ctx.createImageData(canvasWidth, canvasHeight),
    tempData = temp.data,
  
    r = [],
    g = [],
    b = [],
    a = [],
    b32 = [],
    v32 = [];
  
  for (var y = 0; y < canvasHeight; y++) {
    for (var x = 0; x < canvasWidth; x++) {
      var red = x * y & 0xff,
        green = x & 0xff,
        blue = y & 0xff,
        alpha = 255;
  
      r.push(red);
      g.push(green);
      b.push(blue);
      a.push(alpha);
      b32.push((alpha << 24) | (blue << 16) | (green << 8) | red);
      v32.push((red << 24) | (green << 16) | (blue << 8) | alpha);
    }
  }
  
  var mr = r[0],
    mg = g[0],
    mb = b[0],
    ma = a[0],
    mb32 = b32[0],
    mv32 = v32[0];

};
</script>

Preparation code output

<canvas id="canvas" height="256" width="256"></canvas>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
putImageData Ref
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    data[p+0] = r[i];
    data[p+1] = g[i];
    data[p+2] = b[i];
    data[p+3] = a[i];
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
putImageData Direct
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    imageData.data[p+0] = r[i];
    imageData.data[p+1] = g[i];
    imageData.data[p+2] = b[i];
    imageData.data[p+3] = a[i];
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
putImageData Temp
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    tempData[p+0] = r[i];
    tempData[p+1] = g[i];
    tempData[p+2] = b[i];
    tempData[p+3] = a[i];
  }
}
ctx.putImageData(temp, 0, 0);
pending…
32-bit Set
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    data32[i] = b32[i];
  }
}
imageData.data.set(buf8);
ctx.putImageData(imageData, 0, 0);
pending…
32-bit Direct
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    pixelsI32[i] = b32[i];
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
32-bit Uint
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    pixelsU32[i] = b32[i];
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
32-bit DataView
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    view.setUint32(p, v32[i]);
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
putImageData Ref Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    data[p+0] = mr;
    data[p+1] = mg;
    data[p+2] = mb;
    data[p+3] = ma;
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
putImageData Direct Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    imageData.data[p+0] = mr;
    imageData.data[p+1] = mg;
    imageData.data[p+2] = mb;
    imageData.data[p+3] = ma;
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
putImageData Temp Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    tempData[p+0] = mr;
    tempData[p+1] = mg;
    tempData[p+2] = mb;
    tempData[p+3] = ma;
  }
}
ctx.putImageData(temp, 0, 0);
pending…
32-bit Set Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    data32[i] = mb32;
  }
}
imageData.data.set(buf8);
ctx.putImageData(imageData, 0, 0);
pending…
32-bit Direct Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    pixelsI32[i] = mb32;
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
32-bit Uint Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    pixelsU32[i] = mb32;
  }
}
ctx.putImageData(imageData, 0, 0);
pending…
32-bit DataView Mono
for (var i=0, p=0, y=0; y < canvasHeight; y++) {
  for (var x=0; x < canvasWidth; x++, i++, p+=4) {
    view.setUint32(p, mv32);
  }
}
ctx.putImageData(imageData, 0, 0);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments