fabric-pure-browser
Version:
Fabric.js package with no node-specific dependencies (node-canvas, jsdom). The project is published once a day (in case if a new version appears) from 'master' branch of https://github.com/fabricjs/fabric.js repository. You can keep original imports in
353 lines (331 loc) • 13.1 kB
JavaScript
(function(global) {
'use strict';
var fabric = global.fabric || (global.fabric = { }),
extend = fabric.util.object.extend,
filters = fabric.Image.filters,
createClass = fabric.util.createClass;
/**
* Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
* @class fabric.Image.filters.Convolute
* @memberOf fabric.Image.filters
* @extends fabric.Image.filters.BaseFilter
* @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition
* @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
* @example <caption>Sharpen filter</caption>
* var filter = new fabric.Image.filters.Convolute({
* matrix: [ 0, -1, 0,
* -1, 5, -1,
* 0, -1, 0 ]
* });
* object.filters.push(filter);
* object.applyFilters();
* canvas.renderAll();
* @example <caption>Blur filter</caption>
* var filter = new fabric.Image.filters.Convolute({
* matrix: [ 1/9, 1/9, 1/9,
* 1/9, 1/9, 1/9,
* 1/9, 1/9, 1/9 ]
* });
* object.filters.push(filter);
* object.applyFilters();
* canvas.renderAll();
* @example <caption>Emboss filter</caption>
* var filter = new fabric.Image.filters.Convolute({
* matrix: [ 1, 1, 1,
* 1, 0.7, -1,
* -1, -1, -1 ]
* });
* object.filters.push(filter);
* object.applyFilters();
* canvas.renderAll();
* @example <caption>Emboss filter with opaqueness</caption>
* var filter = new fabric.Image.filters.Convolute({
* opaque: true,
* matrix: [ 1, 1, 1,
* 1, 0.7, -1,
* -1, -1, -1 ]
* });
* object.filters.push(filter);
* object.applyFilters();
* canvas.renderAll();
*/
filters.Convolute = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Convolute',
/*
* Opaque value (true/false)
*/
opaque: false,
/*
* matrix for the filter, max 9x9
*/
matrix: [0, 0, 0, 0, 1, 0, 0, 0, 0],
/**
* Fragment source for the brightness program
*/
fragmentSource: {
Convolute_3_1: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[9];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 0);\n' +
'for (float h = 0.0; h < 3.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 3.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 1), uStepH * (h - 1));\n' +
'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 3.0 + w)];\n' +
'}\n' +
'}\n' +
'gl_FragColor = color;\n' +
'}',
Convolute_3_0: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[9];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 1);\n' +
'for (float h = 0.0; h < 3.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 3.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 1.0), uStepH * (h - 1.0));\n' +
'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 3.0 + w)];\n' +
'}\n' +
'}\n' +
'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
'gl_FragColor = color;\n' +
'gl_FragColor.a = alpha;\n' +
'}',
Convolute_5_1: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[25];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 0);\n' +
'for (float h = 0.0; h < 5.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 5.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\n' +
'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 5.0 + w)];\n' +
'}\n' +
'}\n' +
'gl_FragColor = color;\n' +
'}',
Convolute_5_0: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[25];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 1);\n' +
'for (float h = 0.0; h < 5.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 5.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\n' +
'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 5.0 + w)];\n' +
'}\n' +
'}\n' +
'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
'gl_FragColor = color;\n' +
'gl_FragColor.a = alpha;\n' +
'}',
Convolute_7_1: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[49];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 0);\n' +
'for (float h = 0.0; h < 7.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 7.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\n' +
'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 7.0 + w)];\n' +
'}\n' +
'}\n' +
'gl_FragColor = color;\n' +
'}',
Convolute_7_0: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[49];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 1);\n' +
'for (float h = 0.0; h < 7.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 7.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\n' +
'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 7.0 + w)];\n' +
'}\n' +
'}\n' +
'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
'gl_FragColor = color;\n' +
'gl_FragColor.a = alpha;\n' +
'}',
Convolute_9_1: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[81];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 0);\n' +
'for (float h = 0.0; h < 9.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 9.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\n' +
'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 9.0 + w)];\n' +
'}\n' +
'}\n' +
'gl_FragColor = color;\n' +
'}',
Convolute_9_0: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uMatrix[81];\n' +
'uniform float uStepW;\n' +
'uniform float uStepH;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = vec4(0, 0, 0, 1);\n' +
'for (float h = 0.0; h < 9.0; h+=1.0) {\n' +
'for (float w = 0.0; w < 9.0; w+=1.0) {\n' +
'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\n' +
'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 9.0 + w)];\n' +
'}\n' +
'}\n' +
'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
'gl_FragColor = color;\n' +
'gl_FragColor.a = alpha;\n' +
'}',
},
/**
* Constructor
* @memberOf fabric.Image.filters.Convolute.prototype
* @param {Object} [options] Options object
* @param {Boolean} [options.opaque=false] Opaque value (true/false)
* @param {Array} [options.matrix] Filter matrix
*/
/**
* Retrieves the cached shader.
* @param {Object} options
* @param {WebGLRenderingContext} options.context The GL context used for rendering.
* @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
*/
retrieveShader: function(options) {
var size = Math.sqrt(this.matrix.length);
var cacheKey = this.type + '_' + size + '_' + (this.opaque ? 1 : 0);
var shaderSource = this.fragmentSource[cacheKey];
if (!options.programCache.hasOwnProperty(cacheKey)) {
options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
}
return options.programCache[cacheKey];
},
/**
* Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.
*
* @param {Object} options
* @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
*/
applyTo2d: function(options) {
var imageData = options.imageData,
data = imageData.data,
weights = this.matrix,
side = Math.round(Math.sqrt(weights.length)),
halfSide = Math.floor(side / 2),
sw = imageData.width,
sh = imageData.height,
output = options.ctx.createImageData(sw, sh),
dst = output.data,
// go through the destination image pixels
alphaFac = this.opaque ? 1 : 0,
r, g, b, a, dstOff,
scx, scy, srcOff, wt,
x, y, cx, cy;
for (y = 0; y < sh; y++) {
for (x = 0; x < sw; x++) {
dstOff = (y * sw + x) * 4;
// calculate the weighed sum of the source image pixels that
// fall under the convolution matrix
r = 0; g = 0; b = 0; a = 0;
for (cy = 0; cy < side; cy++) {
for (cx = 0; cx < side; cx++) {
scy = y + cy - halfSide;
scx = x + cx - halfSide;
// eslint-disable-next-line max-depth
if (scy < 0 || scy >= sh || scx < 0 || scx >= sw) {
continue;
}
srcOff = (scy * sw + scx) * 4;
wt = weights[cy * side + cx];
r += data[srcOff] * wt;
g += data[srcOff + 1] * wt;
b += data[srcOff + 2] * wt;
// eslint-disable-next-line max-depth
if (!alphaFac) {
a += data[srcOff + 3] * wt;
}
}
}
dst[dstOff] = r;
dst[dstOff + 1] = g;
dst[dstOff + 2] = b;
if (!alphaFac) {
dst[dstOff + 3] = a;
}
else {
dst[dstOff + 3] = data[dstOff + 3];
}
}
}
options.imageData = output;
},
/**
* Return WebGL uniform locations for this filter's shader.
*
* @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
* @param {WebGLShaderProgram} program This filter's compiled shader program.
*/
getUniformLocations: function(gl, program) {
return {
uMatrix: gl.getUniformLocation(program, 'uMatrix'),
uOpaque: gl.getUniformLocation(program, 'uOpaque'),
uHalfSize: gl.getUniformLocation(program, 'uHalfSize'),
uSize: gl.getUniformLocation(program, 'uSize'),
};
},
/**
* Send data from this filter to its shader program's uniforms.
*
* @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
* @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
*/
sendUniformData: function(gl, uniformLocations) {
gl.uniform1fv(uniformLocations.uMatrix, this.matrix);
},
/**
* Returns object representation of an instance
* @return {Object} Object representation of an instance
*/
toObject: function() {
return extend(this.callSuper('toObject'), {
opaque: this.opaque,
matrix: this.matrix
});
}
});
/**
* Returns filter instance from an object representation
* @static
* @param {Object} object Object to create an instance from
* @param {function} [callback] to be invoked after filter creation
* @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
*/
fabric.Image.filters.Convolute.fromObject = fabric.Image.filters.BaseFilter.fromObject;
})(typeof exports !== 'undefined' ? exports : this);