Canvas API calls vs. matrix computing with automatic matrix change detection

JavaScript performance comparison

Revision 5 of this test case created

Preparation code

<canvas width="640" height="480" id="canvas1"></canvas>
<canvas width="640" height="480" id="canvas2"></canvas>
<script>
Benchmark.prototype.setup = function() {
    var transform2d = function(foreign) {
      var matrix = new Float32Array(256 + 6),
        currentPos = 6,
        mat;
   
      function identity() {
        matrix[0] = 1;
        matrix[2] = 0;
        matrix[4] = 0;
        matrix[1] = 0;
        matrix[3] = 1;
        matrix[5] = 0;
        mat.modified = 1;
      }
   
      function translate(x, y) {
        matrix[4] += matrix[0] * x + matrix[2] * y;
        matrix[5] += matrix[1] * x + matrix[3] * y;
        mat.modified = 1;
      }
   
      function scale(sx, sy) {
        matrix[0] *= sx;
        matrix[1] *= sx;
        matrix[2] *= sy;
        matrix[3] *= sy;
        mat.modified = 1;
      }
   
      function rotate(a) {
        var cos = Math.cos(a),
          sin = Math.sin(a),
          mAA = matrix[0] * cos + matrix[2] * sin,
          mAB = matrix[1] * cos + matrix[3] * sin,
          mBA = -matrix[0] * sin + matrix[2] * cos,
          mBB = -matrix[1] * sin + matrix[3] * cos;
   
        matrix[0] = mAA;
        matrix[1] = mAB;
        matrix[2] = mBA;
        matrix[3] = mBB;
        mat.modified = 1;
      }
   
      function push() {
        matrix[currentPos++] = matrix[0];
        matrix[currentPos++] = matrix[1];
        matrix[currentPos++] = matrix[2];
        matrix[currentPos++] = matrix[3];
        matrix[currentPos++] = matrix[4];
        matrix[currentPos++] = matrix[5];
      }
   
      function pop() {
        matrix[5] = matrix[--currentPos];
        matrix[4] = matrix[--currentPos];
        matrix[3] = matrix[--currentPos];
        matrix[2] = matrix[--currentPos];
        matrix[1] = matrix[--currentPos];
        matrix[0] = matrix[--currentPos];
        mat.modified = 1;
      }
   
      function transformContext() {
        foreign.context.setTransform(matrix[0], matrix[1], matrix[2],
          matrix[3], matrix[4], matrix[5]);
        mat.modified = 0;
      }
   
      mat = {
        transformContext: transformContext,
        identity: identity,
        translate: translate,
        scale: scale,
        rotate: rotate,
        push: push,
        pop: pop,
        modified: 0
      };
   
      return mat;
    };
   
    var canvas1 = document.querySelector('#canvas1'),
      ctx1 = canvas1.getContext('2d'),
      mat = transform2d({
        context: ctx1
      }),
      canvas2 = document.querySelector('#canvas2'),
      ctx2 = canvas2.getContext('2d');
   
    ctx1.fillStyle = 'black';
    ctx2.fillStyle = 'black';
   
    mat.identity();
   
    function drawBoxMat(x, y) {
      if (mat.modified) {
        mat.transformContext();
      }
      ctx1.fillRect(0, 0, x, y);
    }
   
    function drawBoxMat2(x, y) {
      ctx1.fillRect(0, 0, x, y);
    }
   
    function drawBoxCanvas(x, y) {
      ctx2.fillRect(0, 0, x, y);
    }
};
</script>

Preparation code output

Test runner

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

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Matrix with push/pop with auto matrix change detection
mat.push();
mat.translate(canvas1.width / 2, canvas1.height / 2);
mat.push();
mat.rotate(Math.PI / 4);
mat.translate(-10, -10);

drawBoxMat(20, 20);
drawBoxMat(30, 30);
drawBoxMat(40, 40);
drawBoxMat(50, 50);
drawBoxMat(60, 60);
mat.pop();

mat.push();
mat.rotate(-Math.PI / 3);
mat.scale(4, 4);
mat.translate(-10, -10);
drawBoxMat(20, 20)
mat.pop();
mat.pop();
pending…
Canvas API with save/restore
ctx2.save();
ctx2.translate(canvas2.width / 2, canvas2.height / 2);
ctx2.save();
ctx2.rotate(Math.PI / 4);
ctx2.translate(-10, -10);

drawBoxCanvas(20, 20);
drawBoxCanvas(30, 30);
drawBoxCanvas(40, 40);
drawBoxCanvas(50, 50);
drawBoxCanvas(60, 60);
ctx2.restore();

ctx2.save();
ctx2.rotate(-Math.PI / 3);
ctx2.scale(4, 4);
ctx2.translate(-10, -10);

drawBoxCanvas(20, 20);
ctx2.restore();
ctx2.restore();
pending…
Matrix with push/pop with manual transform invocation
mat.push();
mat.translate(canvas1.width / 2, canvas1.height / 2);
mat.push();
mat.rotate(Math.PI / 4);
mat.translate(-10, -10);
mat.transformContext();

drawBoxMat2(20, 20);
drawBoxMat2(30, 30);
drawBoxMat2(40, 40);
drawBoxMat2(50, 50);
drawBoxMat2(60, 60);
mat.pop();

mat.push();
mat.rotate(-Math.PI / 3);
mat.scale(4, 4);
mat.translate(-10, -10);

mat.transformContext();
drawBoxMat2(20, 20)
mat.pop();
mat.pop();
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. Here’s a list of current revisions for this page:

0 comments

Add a comment