Skip to content

Instantly share code, notes, and snippets.

@codingdudecom
Last active October 14, 2023 12:34
Show Gist options
  • Save codingdudecom/ba183221d705a23962fcfcd3cae0c63f to your computer and use it in GitHub Desktop.
Save codingdudecom/ba183221d705a23962fcfcd3cae0c63f to your computer and use it in GitHub Desktop.

Online Photo Editing - FabricJS Masking

A while ago I tried implementing a photo mask tool in MockoFun which is based on FabricJS

Check out an working example: https://jsfiddle.net/codingdude/sk84xLh2/

The solution I came up was combining the built in fabric.Image.filters.BlendImage filter with a custom FabricJS brush. Here's a glimpse at my implementation of that.

(function(){
		fabric.Image.filters.BlendImage.prototype.mode = "mask";
		fabric.Image.fromURL("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2NgYGD4DwABBAEAcCBlCwAAAABJRU5ErkJggg==",function(img){
			fabric.Image.filters.BlendImage.prototype.image = img;
		})

		fabric.Image.filters.BlendImage.prototype.fragmentSource["mask"] =
	   \`precision highp float;
		uniform sampler2D uTexture;
		uniform sampler2D uImage;
		uniform vec4 uColor;
		varying vec2 vTexCoord;
		varying vec2 vTexCoord2;

		void main() {
			vec4 color = texture2D(uTexture, vTexCoord);
			vec4 color2 = texture2D(uImage, vTexCoord2);
			color.a *= color2.a;
			gl_FragColor = color;
		}\`;
		fabric.MaskBrush = fabric.util.createClass(fabric.PencilBrush,{
		    initialize: function(options) {
		      this.canvas = options.canvas;
		      this.width = options.width;
		      this.target = options.target;
		      this.shadow = options.shadow;
		      this.blur = options.blur;
		      this.color = options.color;
		      this.targetMaskFilter = options.targetMaskFilter;
		      //"destination-over" ADD
		      //"source-out" REMOVE
		      this.mode = options.mode?options.mode:"source-out";
		      this._points = [];
		      
		    },
			_saveAndTransform: function(ctx) {
				var self = this;
				var v = this.canvas.viewportTransform;
				ctx.save();
				ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
				ctx.filter = self.blur?"blur("+self.blur+"px)":"none";
			},
		    _finalizeAndAddPath: function() {
		      var self = this;
		      var ctx = this.canvas.contextTop;
		      

		      ctx.closePath();
		      if (this.decimate) {
		        this._points = this.decimatePoints(this._points, this.decimate);
		      }
		      var pathData = this.convertPointsToSVGPath(this._points).join('');
		      if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
		        // do not create 0 width/height paths, as they are
		        // rendered inconsistently across browsers
		        // Firefox 4, for example, renders a dot,
		        // whereas Chrome 10 renders nothing
		        this.canvas.requestRenderAll();
		        return;
		      }

		      console.log("here");
		      var r = this.target.getBoundingRect();
			  var imgData = this.canvas.contextTop.getImageData(r.left,r.top,r.width,r.height);

			  var c = document.createElement("canvas");
			  var tempCtx = c.getContext("2d");
			  c.width = r.width;
			  c.height = r.height;
			  if (!self.targetMaskFilter.image){
			  	tempCtx.fillRect(0,0,c.width,c.height);
			  	fabric.Image.fromURL(c.toDataURL(),function(mask){
				  	self.targetMaskFilter.image = mask;
				  	self.target.applyFilters();
				  	self.canvas.requestRenderAll();
				  	self._finalizeAndAddPath();
				  });
			  	
			  	return;
			  }
			  
			  
			  
			  tempCtx.putImageData(imgData,0,0);
			  if (self.targetMaskFilter.image){
			  	tempCtx.globalCompositeOperation = self.mode;
			  	tempCtx.drawImage(self.targetMaskFilter.image.getElement(),0,0,c.width,c.height);
			  }
			  fabric.Image.fromURL(c.toDataURL(),function(mask){
			  	self.targetMaskFilter.image = mask;
			  	self.target.applyFilters();
			  	self.canvas.requestRenderAll();
			  });
		      this.canvas.clearContext(this.canvas.contextTop);
		      this.canvas.requestRenderAll();
		      this._resetShadow();
		    }
		});
	}
)();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment