Hooman Askari
Hooman Askari

Reputation: 1626

FabricJS custom filter plugin works when adding filters to images, but fails to load the canvas via JSON

Version

1.7.3

Test Case

http://jsfiddle.net/human_a/uhe70ju7/

/**
 * Stack blur filter for fabricjs
 * Example:
 * obj.filters.push(new fabric.Image.filters.StackBlur(6));
 * obj.applyFilters(canvas.renderAll.bind(canvas));
 *
 * Heavily inspired by:
 * https://gist.github.com/pierrickouw/2ab679159beee9d80ca6
 * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
 * uses stackBlurCanvasRGBA function but could be swapped be stackBlurCanvasRGB
 * @see: http://www.quasimondo.com/StackBlurForCanvas/StackBlur.js
 */
var fabric = window.fabric,
    extend = fabric.util.object.extend;

var mul_table = [
  512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
  454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
  482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
  437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
  497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
  320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
  446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
  329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
  505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
  399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
  324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
  268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
  451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
  385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
  332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
  289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
];
var shg_table = [
  9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
  17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
  20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
  21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
  21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
];

function BlurStack() {
  this.r = 0;
  this.g = 0;
  this.b = 0;
  this.a = 0;
  this.next = null;
}

fabric.Image.filters.StackBlur = fabric.util.createClass(fabric.Image.filters.BaseFilter, {

  /**
        * Filter type
        */
  type: 'StackBlur',

  /**
        * Constructor
        */
  initialize: function(options) { //radius of the blur

    options = options || {};
    this.radius = options.radius || 0;

  },

  /**
        * Applies blur to canvas element
        */
  applyTo: function(canvasEl) {
    // Don't apply blur if it's zero
    if (!this.radius) return;

    var radius = this.radius;
    var width = canvasEl.width;
    var height = canvasEl.height;
    var context = canvasEl.getContext("2d");
    var imageData;

    imageData = context.getImageData( 0, 0, width, height );

    var pixels = imageData.data;

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

    var div = radius + radius + 1;
    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 = a_in_sum = r_sum = g_sum = b_sum = a_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] );
      a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );

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

      stack = stackStart;

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

      for( i = 1; i < radiusPlus1; i++ ) {
        p = yi + (( widthMinus1 < i ? widthMinus1 : 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;
        a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;

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

        stack = stack.next;
      }


      stackIn = stackStart;
      stackOut = stackEnd;
      for ( x = 0; x < width; x++ ) {
        pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
        if ( pa !== 0 )
        {
          pa = 255 / pa;
          pixels[yi]   = ((r_sum * mul_sum) >> shg_sum) * pa;
          pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
          pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
        } else {
          pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
        }

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

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

        // eslint-disable-next-line
        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]);
        a_in_sum += ( stackIn.a = pixels[p+3]);

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

        stackIn = stackIn.next;

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

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

        stackOut = stackOut.next;

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


    for ( x = 0; x < width; x++ ) {
      g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_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]);
      a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);

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

      stack = stackStart;

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

      yp = width;

      for( i = 1; i <= radius; 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;
        a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;

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

        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+3] = pa = (a_sum * mul_sum) >> shg_sum;
        if ( pa > 0 )
        {
          pa = 255 / pa;
          pixels[p]   = ((r_sum * mul_sum) >> shg_sum ) * pa;
          pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
          pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
        } else {
          pixels[p] = pixels[p+1] = pixels[p+2] = 0;
        }

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

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

        // eslint-disable-next-line
        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]));
        a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));

        stackIn = stackIn.next;

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

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

        stackOut = stackOut.next;

        yi += width;
      }
    }
    context.putImageData( imageData, 0, 0 );
  },

  /**
        * Returns object representation of an instance
        */
  toObject: function() {
    return extend(this.callSuper('toObject'), {
      radius: this.radius
    });
  }
});

fabric.Image.filters.StackBlur.fromObject = function() {
  return new fabric.Image.filters.StackBlur()
};
/* END OF STACK BLUR FILTER PLUGIN */






/* Start loading the canvas */
const canvas = new fabric.Canvas();
const el = document.getElementById('my-canvas');
const button1 = document.getElementById('load-canvas1');
const button2 = document.getElementById('load-canvas2');
const addBlur = document.getElementById('add-blur');


const json1 = {"background":"rgba(255, 255, 255, 1)","height":480,"objects":[{"alignX":"none","alignY":"none","angle":0,"backgroundColor":"","crossOrigin":"Anonymous","evented":true,"fill":"rgb(0,0,0)","fillRule":"nonzero","filters":[{"radius":21,"type":"StackBlur"}],"flipX":false,"flipY":false,"globalCompositeOperation":"source-over","height":720,"itemThumb":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","itemTitle":"Image layer","left":360,"lockMovementX":false,"lockMovementY":false,"lockRotation":false,"lockScalingX":false,"lockScalingY":false,"lockUniScaling":true,"meetOrSlice":"meet","objType":"item","opacity":1,"originX":"center","originY":"center","scaleX":1,"scaleY":1,"selectable":true,"skewX":0,"skewY":0,"src":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","stroke":"","strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"strokeWidth":0,"timestamp":1485254754,"top":241,"type":"image","uuid":"e19bcfb3-4993-4709-bca3-ccf5be20c62f","visible":true,"width":1080}],"width":720}

const json2 = {"background":"rgba(255, 255, 255, 1)","height":480,"hoverCursor":"move","id":"mainCanvas","lastModified":1485254765,"objects":[{"alignX":"none","alignY":"none","angle":0,"backgroundColor":"","crossOrigin":"Anonymous","evented":true,"fill":"rgb(0,0,0)","fillRule":"nonzero","filters":[],"flipX":false,"flipY":false,"globalCompositeOperation":"source-over","height":720,"itemThumb":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","itemTitle":"Image layer","left":360,"lockMovementX":false,"lockMovementY":false,"lockRotation":false,"lockScalingX":false,"lockScalingY":false,"lockUniScaling":true,"meetOrSlice":"meet","objType":"item","opacity":1,"originX":"center","originY":"center","scaleX":1,"scaleY":1,"selectable":true,"skewX":0,"skewY":0,"src":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","stroke":"","strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"strokeWidth":0,"timestamp":1485254754,"top":241,"type":"image","uuid":"e19bcfb3-4993-4709-bca3-ccf5be20c62f","visible":true,"width":1080}],"projectDesc":"","projectName":"Untitled Project","projectPublished":false,"projectTags":"","publicDemo":false,"textTemplate":false,"timestamp":1485254742,"width":720}

// Load canvas that contains an image with blur
const loadCanvas1 = function() {
	canvas.initialize(el, {
		backgroundColor: '#FFFFFF',
    enableRetinaScaling: true,
    preserveObjectStacking: true,
    controlsAboveOverlay: false,
    selectionCompatibility: true,
    stopContextMenu: true,
    fireRightClick: true,
    width: 720,
    height: 480,
	});
	canvas.loadFromJSON( JSON.stringify(json1), canvas.renderAll.bind(canvas) );
};
button1.addEventListener( 'click', loadCanvas1 );



// Load canvas WITHOUT blur
const loadCanvas2 = function() {
	canvas.initialize(el, {
    backgroundColor: '#FFFFFF',
    enableRetinaScaling: true,
    preserveObjectStacking: true,
    controlsAboveOverlay: false,
    selectionCompatibility: true,
    stopContextMenu: true,
    fireRightClick: true,
    width: 720,
    height: 480,
	});
	canvas.loadFromJSON( JSON.stringify(json2), ()=>{
  	canvas.renderAll.bind(canvas)
    canvas.setActiveObject(canvas.getObjects()[0])
  } );
};
button2.addEventListener( 'click', loadCanvas2 );

// Add blur event
const addBlurFunc = function() {
	if(canvas.getActiveObject()) {
      canvas.getActiveObject().filters.push(new fabric.Image.filters.StackBlur({radius: 30}));
    	canvas.getActiveObject().applyFilters(canvas.renderAll.bind(canvas));

			canvas.renderAll();
  }
}
addBlur.addEventListener( 'click', addBlurFunc );
button {
	border: 0 none;
	background: #fff;
	border-radius: 5px;
	cursor: pointer;
	color: #333;
	box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
	padding: 11px 22px;
	font-weight: 600;
	font-size: 13px;
	letter-spacing: 1px;
	margin: 10px auto;
	outline: 0 none;
}

button.blue {
  background: #1877FF;
  color: #fff;
}

button.red {
  background: #C35A2D;
  color: #fff;
}

button.green {
  background: #65C879;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.3/fabric.min.js"></script>
<canvas id="my-canvas"></canvas>
<div>
  <button id="load-canvas2" class="green">LOAD CANVAS</button>
  <button id="add-blur" class="blue">ADD BLUR</button>
</div>
<button id="load-canvas1" class="red">LOAD WITH BLUR</button>

Steps to reproduce

Click on the green button to load a canvas via JSON (const json2) and then click on the blue button to add StackBlur to the image inside the project. You can see that the StackBlur filter works fine.

Now click on the red button, it tries to load the other JSON (const json1) which contains an image with StackBlur filter assigned to it. But this time it fails to load the project completely, no errors shown.

Expected Behavior

If the StackBlur filter works when assigning it to the image, it should also work when loading a saved project via JSON.

Actual Behavior

It fails to load a canvas if it contains any elements with StackBlur filter assigned to them.

PS

I created this StackBlur filter plugin based on the following projects: https://gist.github.com/pierrickouw/2ab679159beee9d80ca6 http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html

You can see my gist here: https://gist.github.com/human-a/25b7f58565b89de4e999ef2f4de1982c

Upvotes: 0

Views: 684

Answers (1)

Hooman Askari
Hooman Askari

Reputation: 1626

This question is answered here: https://github.com/kangax/fabric.js/issues/3645

The corrected version of the plugin could be found here: https://gist.github.com/human-a/25b7f58565b89de4e999ef2f4de1982c

/**
 * Stack blur filter for fabricjs
 * Example:
 * obj.filters.push(new fabric.Image.filters.StackBlur(6));
 * obj.applyFilters(canvas.renderAll.bind(canvas));
 *
 * Heavily inspired by:
 * https://gist.github.com/pierrickouw/2ab679159beee9d80ca6
 * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
 * uses stackBlurCanvasRGBA function but could be swapped be stackBlurCanvasRGB
 * @see: http://www.quasimondo.com/StackBlurForCanvas/StackBlur.js
 */
var fabric = window.fabric,
    extend = fabric.util.object.extend;

var mul_table = [
  512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
  454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
  482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
  437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
  497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
  320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
  446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
  329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
  505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
  399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
  324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
  268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
  451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
  385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
  332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
  289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
];
var shg_table = [
  9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
  17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
  20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
  21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
  21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
];

function BlurStack() {
  this.r = 0;
  this.g = 0;
  this.b = 0;
  this.a = 0;
  this.next = null;
}

fabric.Image.filters.StackBlur = fabric.util.createClass(fabric.Image.filters.BaseFilter, {

  /**
        * Filter type
        */
  type: 'StackBlur',

  /**
        * Constructor
        */
  initialize: function(options) { //radius of the blur

    options = options || {};
    this.radius = options.radius || 0;

  },

  /**
        * Applies blur to canvas element
        */
  applyTo: function(canvasEl) {
    // Don't apply blur if it's zero
    if (!this.radius) return;

    var radius = this.radius;
    var width = canvasEl.width;
    var height = canvasEl.height;
    var context = canvasEl.getContext("2d");
    var imageData;

    imageData = context.getImageData( 0, 0, width, height );

    var pixels = imageData.data;

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

    var div = radius + radius + 1;
    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 = a_in_sum = r_sum = g_sum = b_sum = a_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] );
      a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );

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

      stack = stackStart;

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

      for( i = 1; i < radiusPlus1; i++ ) {
        p = yi + (( widthMinus1 < i ? widthMinus1 : 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;
        a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;

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

        stack = stack.next;
      }


      stackIn = stackStart;
      stackOut = stackEnd;
      for ( x = 0; x < width; x++ ) {
        pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
        if ( pa !== 0 )
        {
          pa = 255 / pa;
          pixels[yi]   = ((r_sum * mul_sum) >> shg_sum) * pa;
          pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
          pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
        } else {
          pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
        }

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

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

        // eslint-disable-next-line
        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]);
        a_in_sum += ( stackIn.a = pixels[p+3]);

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

        stackIn = stackIn.next;

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

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

        stackOut = stackOut.next;

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


    for ( x = 0; x < width; x++ ) {
      g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_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]);
      a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);

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

      stack = stackStart;

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

      yp = width;

      for( i = 1; i <= radius; 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;
        a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;

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

        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+3] = pa = (a_sum * mul_sum) >> shg_sum;
        if ( pa > 0 )
        {
          pa = 255 / pa;
          pixels[p]   = ((r_sum * mul_sum) >> shg_sum ) * pa;
          pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
          pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
        } else {
          pixels[p] = pixels[p+1] = pixels[p+2] = 0;
        }

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

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

        // eslint-disable-next-line
        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]));
        a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));

        stackIn = stackIn.next;

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

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

        stackOut = stackOut.next;

        yi += width;
      }
    }
    context.putImageData( imageData, 0, 0 );
  },

  /**
        * Returns object representation of an instance
        */
  toObject: function() {
    return extend(this.callSuper('toObject'), {
      radius: this.radius
    });
  }
});

fabric.Image.filters.StackBlur.fromObject = fabric.Image.filters.BaseFilter.fromObject
/* END OF STACK BLUR FILTER PLUGIN */






/* Start loading the canvas */
const canvas = new fabric.Canvas();
const el = document.getElementById('my-canvas');
const button1 = document.getElementById('load-canvas1');
const button2 = document.getElementById('load-canvas2');
const addBlur = document.getElementById('add-blur');


const json1 = {"background":"rgba(255, 255, 255, 1)","height":480,"objects":[{"alignX":"none","alignY":"none","angle":0,"backgroundColor":"","crossOrigin":"Anonymous","evented":true,"fill":"rgb(0,0,0)","fillRule":"nonzero","filters":[{"radius":21,"type":"StackBlur"}],"flipX":false,"flipY":false,"globalCompositeOperation":"source-over","height":720,"itemThumb":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","itemTitle":"Image layer","left":360,"lockMovementX":false,"lockMovementY":false,"lockRotation":false,"lockScalingX":false,"lockScalingY":false,"lockUniScaling":true,"meetOrSlice":"meet","objType":"item","opacity":1,"originX":"center","originY":"center","scaleX":1,"scaleY":1,"selectable":true,"skewX":0,"skewY":0,"src":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","stroke":"","strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"strokeWidth":0,"timestamp":1485254754,"top":241,"type":"image","uuid":"e19bcfb3-4993-4709-bca3-ccf5be20c62f","visible":true,"width":1080}],"width":720}

const json2 = {"background":"rgba(255, 255, 255, 1)","height":480,"hoverCursor":"move","id":"mainCanvas","lastModified":1485254765,"objects":[{"alignX":"none","alignY":"none","angle":0,"backgroundColor":"","crossOrigin":"Anonymous","evented":true,"fill":"rgb(0,0,0)","fillRule":"nonzero","filters":[],"flipX":false,"flipY":false,"globalCompositeOperation":"source-over","height":720,"itemThumb":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","itemTitle":"Image layer","left":360,"lockMovementX":false,"lockMovementY":false,"lockRotation":false,"lockScalingX":false,"lockScalingY":false,"lockUniScaling":true,"meetOrSlice":"meet","objType":"item","opacity":1,"originX":"center","originY":"center","scaleX":1,"scaleY":1,"selectable":true,"skewX":0,"skewY":0,"src":"https://images.unsplash.com/photo-1485239269752-c4fb2b79e7bd?ixlib=rb-0.3.5…crop=entropy&cs=tinysrgb&w=1080&fit=max&s=4f605a4cfd9130ff74b49f5b61d44436","stroke":"","strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"strokeWidth":0,"timestamp":1485254754,"top":241,"type":"image","uuid":"e19bcfb3-4993-4709-bca3-ccf5be20c62f","visible":true,"width":1080}],"projectDesc":"","projectName":"Untitled Project","projectPublished":false,"projectTags":"","publicDemo":false,"textTemplate":false,"timestamp":1485254742,"width":720}

// Load canvas that contains an image with blur
const loadCanvas1 = function() {
	canvas.initialize(el, {
		backgroundColor: '#FFFFFF',
    enableRetinaScaling: true,
    preserveObjectStacking: true,
    controlsAboveOverlay: false,
    selectionCompatibility: true,
    stopContextMenu: true,
    fireRightClick: true,
    width: 720,
    height: 480,
	});
	canvas.loadFromJSON( JSON.stringify(json1), canvas.renderAll.bind(canvas) );
};
button1.addEventListener( 'click', loadCanvas1 );



// Load canvas WITHOUT blur
const loadCanvas2 = function() {
	canvas.initialize(el, {
    backgroundColor: '#FFFFFF',
    enableRetinaScaling: true,
    preserveObjectStacking: true,
    controlsAboveOverlay: false,
    selectionCompatibility: true,
    stopContextMenu: true,
    fireRightClick: true,
    width: 720,
    height: 480,
	});
	canvas.loadFromJSON( JSON.stringify(json2), ()=>{
  	canvas.renderAll.bind(canvas)
    canvas.setActiveObject(canvas.getObjects()[0])
  } );
};
button2.addEventListener( 'click', loadCanvas2 );

// Add blur event
const addBlurFunc = function() {
	if(canvas.getActiveObject()) {
      canvas.getActiveObject().filters.push(new fabric.Image.filters.StackBlur({radius: 30}));
    	canvas.getActiveObject().applyFilters(canvas.renderAll.bind(canvas));

			canvas.renderAll();
  }
}
addBlur.addEventListener( 'click', addBlurFunc );
button {
	border: 0 none;
	background: #fff;
	border-radius: 5px;
	cursor: pointer;
	color: #333;
	box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
	padding: 11px 22px;
	font-weight: 600;
	font-size: 13px;
	letter-spacing: 1px;
	margin: 10px auto;
	outline: 0 none;
}

button.blue {
  background: #1877FF;
  color: #fff;
}

button.red {
  background: #C35A2D;
  color: #fff;
}

button.green {
  background: #65C879;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.3/fabric.min.js"></script>
<canvas id="my-canvas"></canvas>
<div>
  <button id="load-canvas2" class="green">LOAD CANVAS</button>
  <button id="add-blur" class="blue">ADD BLUR</button>
</div>
<button id="load-canvas1" class="red">LOAD WITH BLUR</button>

Upvotes: 1

Related Questions