SuperfastBlur vs StackBoxBlur vs IntegralImageBlur vs StackBlur (v20)

Revision 20 of this benchmark created on


Preparation HTML

<script src="http://www.quasimondo.com/BoxBlurForCanvas/StackBoxBlur.js" language="javascript"></script>
<script src="http://www.quasimondo.com/BoxBlurForCanvas/FastBlur.js" language="javascript"></script>
<script src="http://www.quasimondo.com/StackBlurForCanvas/StackBlur.js" language="javascript"></script>
<script src="http://www.quasimondo.com/IntegralImageForCanvas/IntegralImage.js" language="javascript"></script>
<div id="canvasHolder" style="position:absolute;left:20px; top:20px;text-align:center;font-size:10px;">
<canvas height="375" width="500" style="width: 500px; height: 375px;" id="canvas">
</canvas></div>

Test runner

Ready to run.

Testing in
TestOps/sec
Blurring with StackBoxBlur (1 iteration)
stackBoxBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 1);
ready
Blurring with SuperfastBlur (1 iteration)
boxBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 1);
ready
Blurring with StackBlur
stackBlurCanvasRGB('canvas', 0, 0, 500, 375, 8);
ready
Blurring with StackBoxBlur (2 iterations)
stackBoxBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 2);
ready
Blurring with SuperfastBlur (2 iterations)
boxBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 2);
ready
Blurring with IntegralImageBlur (1 iteration)
integralBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 1);
ready
Blurring with IntegralImageBlur (2 iteration)
integralBlurCanvasRGB('canvas', 0, 0, 500, 375, 8, 2);
ready
erez
function stackBlurCanvasRGB1(id, top_x, top_y, width, height, radius) {
  if (isNaN(radius) || radius < 1) return;
  radius |= 0;

  var canvas = document.getElementById(id);
  var context = canvas.getContext("2d");
  var imageData;

  try {
    try {
      imageData = context.getImageData(top_x, top_y, width, height);
    } catch (e) {

      // NOTE: this part is supposedly only needed if you want to work with local files
      // so it might be okay to remove the whole try/catch block and just use
      // imageData = context.getImageData( top_x, top_y, width, height );
      try {
        netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
        imageData = context.getImageData(top_x, top_y, width, height);
      } catch (e) {
        alert("Cannot access local image");
        throw new Error("unable to access local image data: " + e);
        return;
      }
    }
  } catch (e) {
    alert("Cannot access image");
    throw new Error("unable to access image data: " + e);
  }

  var pixels = imageData.data;

  var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum,
    r_out_sum, g_out_sum, b_out_sum,
    r_in_sum, g_in_sum, b_in_sum,
    pr, pg, pb, rbs;

  var div = radius + radius + 1;
  var w4 = width << 2;
  var widthMinus1 = width - 1;
  var heightMinus1 = height - 1;
  var radiusPlus1 = radius + 1;
  var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;

  var stackStart = new BlurStack();
  var stack = stackStart;
  for (i = 1; i < div; i++) {
    stack = stack.next = new BlurStack();
    if (i == radiusPlus1) var stackEnd = stack;
  }
  stack.next = stackStart;
  var stackIn = null;
  var stackOut = null;

  yw = yi = 0;

  var mul_sum = mul_table[radius];
  var shg_sum = shg_table[radius];

  for (y = 0; y < height; y++) {
    r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;

    r_out_sum = radiusPlus1 * (pr = pixels[yi]);
    g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
    b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);

    r_sum += sumFactor * pr;
    g_sum += sumFactor * pg;
    b_sum += sumFactor * pb;

    stack = stackStart;

    for (i = 0; i < radiusPlus1; i++) {
      stack.r = pr;
      stack.g = pg;
      stack.b = pb;
      stack = stack.next;
    }

    var start = 1;
    var end = Math.min(widthMinus1, 1);

    for (i = start; i <= end; i++) {
      //p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); @@erez
      p = yi + i << 2;
      r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
      g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
      b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;

      r_in_sum += pr;
      g_in_sum += pg;
      b_in_sum += pb;

      stack = stack.next;
    }

    start = i;
    end = radiusPlus1;
    var w2 = widthMinus1 << 2;
    for (i = start; i < end; i++) {
      p = yi + w2;
      r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
      g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
      b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;

      r_in_sum += pr;
      g_in_sum += pg;
      b_in_sum += pb;

      stack = stack.next;
    }



    stackIn = stackStart;
    stackOut = stackEnd;
    for (x = 0; x < width; x++) {
      pixels[yi] = (r_sum * mul_sum) >> shg_sum;
      pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum;
      pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum;

      r_sum -= r_out_sum;
      g_sum -= g_out_sum;
      b_sum -= b_out_sum;

      r_out_sum -= stackIn.r;
      g_out_sum -= stackIn.g;
      b_out_sum -= stackIn.b;

      p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;

      r_in_sum += (stackIn.r = pixels[p]);
      g_in_sum += (stackIn.g = pixels[p + 1]);
      b_in_sum += (stackIn.b = pixels[p + 2]);

      r_sum += r_in_sum;
      g_sum += g_in_sum;
      b_sum += b_in_sum;

      stackIn = stackIn.next;

      r_out_sum += (pr = stackOut.r);
      g_out_sum += (pg = stackOut.g);
      b_out_sum += (pb = stackOut.b);

      r_in_sum -= pr;
      g_in_sum -= pg;
      b_in_sum -= pb;

      stackOut = stackOut.next;

      yi += 4;
    }
    yw += width;
  }


  for (x = 0; x < width; x++) {
    g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;

    yi = x << 2;
    r_out_sum = radiusPlus1 * (pr = pixels[yi]);
    g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
    b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);

    r_sum += sumFactor * pr;
    g_sum += sumFactor * pg;
    b_sum += sumFactor * pb;

    stack = stackStart;

    for (i = 0; i < radiusPlus1; i++) {
      stack.r = pr;
      stack.g = pg;
      stack.b = pb;
      stack = stack.next;
    }

    yp = width;



    var start = 1;
    var end = Math.min(heightMinus1, radius);

    for (i = start; i < end; i++) {
      yi = (yp + x) << 2;

      r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
      g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
      b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;

      r_in_sum += pr;
      g_in_sum += pg;
      b_in_sum += pb;

      stack = stack.next;

      //if( i < heightMinus1 )
      //{
      yp += width;
      //}
    }
    start = i;
    end = radius;

    for (i = start; i < end; i++) {
      yi = (yp + x) << 2;

      r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
      g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
      b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;

      r_in_sum += pr;
      g_in_sum += pg;
      b_in_sum += pb;

      stack = stack.next;

      //if( i < heightMinus1 )
      //{
      //yp += width;
      //}
    }


    yi = x;
    stackIn = stackStart;
    stackOut = stackEnd;
    for (y = 0; y < height; y++) {
      p = yi << 2;
      pixels[p] = (r_sum * mul_sum) >> shg_sum;
      pixels[p + 1] = (g_sum * mul_sum) >> shg_sum;
      pixels[p + 2] = (b_sum * mul_sum) >> shg_sum;

      r_sum -= r_out_sum;
      g_sum -= g_out_sum;
      b_sum -= b_out_sum;

      r_out_sum -= stackIn.r;
      g_out_sum -= stackIn.g;
      b_out_sum -= stackIn.b;

      p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;

      r_sum += (r_in_sum += (stackIn.r = pixels[p]));
      g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]));
      b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]));

      stackIn = stackIn.next;

      r_out_sum += (pr = stackOut.r);
      g_out_sum += (pg = stackOut.g);
      b_out_sum += (pb = stackOut.b);

      r_in_sum -= pr;
      g_in_sum -= pg;
      b_in_sum -= pb;

      stackOut = stackOut.next;

      yi += width;
    }
  }

  context.putImageData(imageData, top_x, top_y);

}

stackBlurCanvasRGB1('canvas', 0, 0, 500, 375, 8, 1);
ready

Revisions

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