mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-04-29 01:34:42 +00:00
parent
ea135812b1
commit
6962f8e6f2
6 changed files with 252 additions and 6 deletions
1
build.js
1
build.js
|
|
@ -214,6 +214,7 @@ var filesToInclude = [
|
|||
ifSpecifiedInclude('image_filters', 'src/filters/removecolor_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/filter_generator.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/blendcolor_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/blendimage_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/resize_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/contrast_filter.class.js'),
|
||||
ifSpecifiedInclude('image_filters', 'src/filters/saturate_filter.class.js'),
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
originalImageData: originalImageData,
|
||||
canvasEl: targetCanvas,
|
||||
ctx: ctx,
|
||||
filterBackend: this,
|
||||
};
|
||||
filters.forEach(function(filter) { filter.applyTo(pipelineState); });
|
||||
if (pipelineState.imageData.width !== sourceWidth || pipelineState.imageData.height !== sourceHeight) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@
|
|||
* @memberOf fabric.Image.filters
|
||||
* @extends fabric.Image.filters.BaseFilter
|
||||
* @example
|
||||
* var filter = new fabric.Image.filters.Blend({
|
||||
* var filter = new fabric.Image.filters.BlendColor({
|
||||
* color: '#000',
|
||||
* mode: 'multiply'
|
||||
* });
|
||||
*
|
||||
* var filter = new fabric.Image.filters.BlendColor({
|
||||
* var filter = new fabric.Image.filters.BlendImage({
|
||||
* image: fabricImageObject,
|
||||
* mode: 'multiply',
|
||||
* alpha: 0.5
|
||||
|
|
@ -289,7 +289,7 @@
|
|||
* @static
|
||||
* @param {Object} object Object to create an instance from
|
||||
* @param {function} [callback] to be invoked after filter creation
|
||||
* @return {fabric.Image.filters.BlendColor} Instance of fabric.Image.filters.Blend
|
||||
* @return {fabric.Image.filters.BlendColor} Instance of fabric.Image.filters.BlendColor
|
||||
*/
|
||||
fabric.Image.filters.BlendColor.fromObject = fabric.Image.filters.BaseFilter.fromObject;
|
||||
|
||||
|
|
|
|||
243
src/filters/blendimage_filter.class.js
Normal file
243
src/filters/blendimage_filter.class.js
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
(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 color operation.
|
||||
**/
|
||||
alpha: 1,
|
||||
|
||||
vertexSource: 'attribute vec2 aPosition;\n' +
|
||||
'attribute vec2 aTexCoord;\n' +
|
||||
'varying vec2 vTexCoord;\n' +
|
||||
'varying vec2 vTexCoord2;\n' +
|
||||
'uniform mat3 uTransformMatrix;\n' +
|
||||
'void main() {\n' +
|
||||
'vTexCoord = aTexCoord;\n' +
|
||||
'vTexCoord2 = (uTransformMatrix * vec3(aTexCoord, 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 = options.imageData.width,
|
||||
height = options.imageData.height,
|
||||
tr, tg, tb, ta,
|
||||
r, g, b, a,
|
||||
canvas1, context, image = this.image, blendData;
|
||||
|
||||
if (!resources.blendImage) {
|
||||
resources.blendImage = document.createElement('canvas');
|
||||
}
|
||||
canvas1 = resources.blendImage;
|
||||
if (canvas1.width !== width || canvas1.height !== height) {
|
||||
canvas1.width = width;
|
||||
canvas1.height = height;
|
||||
}
|
||||
context = canvas1.getContext('2d');
|
||||
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);
|
||||
|
|
@ -104,7 +104,7 @@
|
|||
},
|
||||
|
||||
simpleBlur: function(options) {
|
||||
var resources = fabric.filterBackend.resources, canvas1, canvas2,
|
||||
var resources = options.filterBackend.resources, canvas1, canvas2,
|
||||
width = options.imageData.width,
|
||||
height = options.imageData.height;
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
squareVertices: this.squareVertices,
|
||||
programCache: this.programCache,
|
||||
pass: 0,
|
||||
filterBackend: this
|
||||
};
|
||||
var tempFbo = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, tempFbo);
|
||||
|
|
@ -208,8 +209,8 @@
|
|||
createTexture: function(gl, width, height, textureImageSource) {
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
if (textureImageSource) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue