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
247 lines (224 loc) • 8.2 kB
JavaScript
(function(global) {
'use strict';
var fabric = global.fabric,
filters = fabric.Image.filters,
createClass = fabric.util.createClass;
/**
* Image Blend filter class
* @class fabric.Image.filter.BlendImage
* @memberOf fabric.Image.filters
* @extends fabric.Image.filters.BaseFilter
* @example
* var filter = new fabric.Image.filters.BlendColor({
* color: '#000',
* mode: 'multiply'
* });
*
* var filter = new fabric.Image.filters.BlendImage({
* image: fabricImageObject,
* mode: 'multiply',
* alpha: 0.5
* });
* object.filters.push(filter);
* object.applyFilters();
* canvas.renderAll();
*/
filters.BlendImage = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.BlendImage.prototype */ {
type: 'BlendImage',
/**
* Color to make the blend operation with. default to a reddish color since black or white
* gives always strong result.
**/
image: null,
/**
* Blend mode for the filter: one of multiply, add, diff, screen, subtract,
* darken, lighten, overlay, exclusion, tint.
**/
mode: 'multiply',
/**
* alpha value. represent the strength of the blend image operation.
* not implemented.
**/
alpha: 1,
vertexSource: 'attribute vec2 aPosition;\n' +
'varying vec2 vTexCoord;\n' +
'varying vec2 vTexCoord2;\n' +
'uniform mat3 uTransformMatrix;\n' +
'void main() {\n' +
'vTexCoord = aPosition;\n' +
'vTexCoord2 = (uTransformMatrix * vec3(aPosition, 1.0)).xy;\n' +
'gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\n' +
'}',
/**
* Fragment source for the Multiply program
*/
fragmentSource: {
multiply: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform sampler2D uImage;\n' +
'uniform vec4 uColor;\n' +
'varying vec2 vTexCoord;\n' +
'varying vec2 vTexCoord2;\n' +
'void main() {\n' +
'vec4 color = texture2D(uTexture, vTexCoord);\n' +
'vec4 color2 = texture2D(uImage, vTexCoord2);\n' +
'color.rgba *= color2.rgba;\n' +
'gl_FragColor = color;\n' +
'}',
mask: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform sampler2D uImage;\n' +
'uniform vec4 uColor;\n' +
'varying vec2 vTexCoord;\n' +
'varying vec2 vTexCoord2;\n' +
'void main() {\n' +
'vec4 color = texture2D(uTexture, vTexCoord);\n' +
'vec4 color2 = texture2D(uImage, vTexCoord2);\n' +
'color.a = color2.a;\n' +
'gl_FragColor = color;\n' +
'}',
},
/**
* 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 cacheKey = this.type + '_' + this.mode;
var shaderSource = this.fragmentSource[this.mode];
if (!options.programCache.hasOwnProperty(cacheKey)) {
options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
}
return options.programCache[cacheKey];
},
applyToWebGL: function(options) {
// load texture to blend.
var gl = options.context,
texture = this.createTexture(options.filterBackend, this.image);
this.bindAdditionalTexture(gl, texture, gl.TEXTURE1);
this.callSuper('applyToWebGL', options);
this.unbindAdditionalTexture(gl, gl.TEXTURE1);
},
createTexture: function(backend, image) {
return backend.getCachedTexture(image.cacheKey, image._element);
},
/**
* Calculate a transformMatrix to adapt the image to blend over
* @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.
*/
calculateMatrix: function() {
var image = this.image,
width = image._element.width,
height = image._element.height;
return [
1 / image.scaleX, 0, 0,
0, 1 / image.scaleY, 0,
-image.left / width, -image.top / height, 1
];
},
/**
* Apply the Blend 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,
resources = options.filterBackend.resources,
data = imageData.data, iLen = data.length,
width = imageData.width,
height = imageData.height,
tr, tg, tb, ta,
r, g, b, a,
canvas1, context, image = this.image, blendData;
if (!resources.blendImage) {
resources.blendImage = fabric.util.createCanvasElement();
}
canvas1 = resources.blendImage;
context = canvas1.getContext('2d');
if (canvas1.width !== width || canvas1.height !== height) {
canvas1.width = width;
canvas1.height = height;
}
else {
context.clearRect(0, 0, width, height);
}
context.setTransform(image.scaleX, 0, 0, image.scaleY, image.left, image.top);
context.drawImage(image._element, 0, 0, width, height);
blendData = context.getImageData(0, 0, width, height).data;
for (var i = 0; i < iLen; i += 4) {
r = data[i];
g = data[i + 1];
b = data[i + 2];
a = data[i + 3];
tr = blendData[i];
tg = blendData[i + 1];
tb = blendData[i + 2];
ta = blendData[i + 3];
switch (this.mode) {
case 'multiply':
data[i] = r * tr / 255;
data[i + 1] = g * tg / 255;
data[i + 2] = b * tb / 255;
data[i + 3] = a * ta / 255;
break;
case 'mask':
data[i + 3] = ta;
break;
}
}
},
/**
* 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 {
uTransformMatrix: gl.getUniformLocation(program, 'uTransformMatrix'),
uImage: gl.getUniformLocation(program, 'uImage'),
};
},
/**
* 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) {
var matrix = this.calculateMatrix();
gl.uniform1i(uniformLocations.uImage, 1); // texture unit 1.
gl.uniformMatrix3fv(uniformLocations.uTransformMatrix, false, matrix);
},
/**
* Returns object representation of an instance
* @return {Object} Object representation of an instance
*/
toObject: function() {
return {
type: this.type,
image: this.image && this.image.toObject(),
mode: this.mode,
alpha: this.alpha
};
}
});
/**
* 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.BlendImage} Instance of fabric.Image.filters.BlendImage
*/
fabric.Image.filters.BlendImage.fromObject = function(object, callback) {
fabric.Image.fromObject(object.image, function(image) {
var options = fabric.util.object.clone(object);
options.image = image;
callback(new fabric.Image.filters.BlendImage(options));
});
};
})(typeof exports !== 'undefined' ? exports : this);