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();
}
});
}
)();