如何通过在 fabric js 中传递颜色代码来创建自定义过滤器
how to create a custom filter by passing a color code in fabric js
我正在尝试创建一个它应该喜欢接受颜色代码的自定义过滤器。
这是我的代码。
工作正常。
fabric.Image.fromURL('pug.jpg', function(img) {
img.filters.push(
new fabric.Image.filters.Sepia(),
new fabric.Image.filters.Brightness({ brightness: 100 }));
img.applyFilters(canvas.renderAll.bind(canvas));
canvas.add(img);
});
现在,我需要创建一个具有特定颜色代码的滤镜。
我发现的是
fabric.Image.filters.Redify = fabric.util.createClass({
type: 'Redify',
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data;
for (var i = 0, len = data.length; i < len; i += 4) {
data[i + 1] = 0;
data[i + 2] = 0;
}
context.putImageData(imageData, 0, 0);
}
});
fabric.Image.filters.Redify.fromObject = function(object) {
return new fabric.Image.filters.Redify(object);
};
我需要解释一下 for 循环的作用...还请解释一下我如何传递颜色代码。
您发现的 redify 滤镜并不是真正的 colorify 滤镜。正如您从代码中看到的那样,它正在杀死绿色和蓝色通道,只留下图像的红色。这与应用红色着色效果不同。
您可以使用相同的方式创建蓝化和绿化滤镜,只需更改幸存的频道即可:
fabric.Image.filters.Greenify= fabric.util.createClass({
type: 'greenify',
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data;
for (var i = 0, len = data.length; i < len; i += 4) {
//kill red
data[i] = 0;
//kill blue
data[i + 2] = 0;
}
context.putImageData(imageData, 0, 0);
}
});
要创建一个彩色滤镜,首先你必须知道如何去做。我亲自检查了 GIMP 的 colorify 滤镜是如何工作的:
https://docs.gimp.org/en/plug-in-colorify.html
1) 根据亮度使图像灰度化
2) 乘以你想要的颜色的灰度级
这或多或少等于按灰度和乘法顺序应用现有的 fabricjs 过滤器。
var canvas = new fabric.Canvas("c");
fabric.Image.filters.Luminosity = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Luminosity.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Luminosity',
/**
* Applies filter to canvas element
* @memberOf fabric.Image.filters.Grayscale.prototype
* @param {Object} canvasEl Canvas element to apply filter to
*/
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
len = imageData.width * imageData.height * 4,
index = 0,
average;
while (index < len) {
//Luminosity = 0.21 × R + 0.72 × G + 0.07 × B
average = (0.21 * data[index] + 0.72 * data[index + 1] + 0.07 * data[index + 2]);
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
index += 4;
}
context.putImageData(imageData, 0, 0);
}
});
/**
* Returns filter instance from an object representation
* @static
* @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
*/
fabric.Image.filters.Grayscale.fromObject = function() {
return new fabric.Image.filters.Grayscale();
};
fabric.Image.fromURL("http://fabricjs.com/assets/pug.jpg", function(img) {
img.filters.push(new fabric.Image.filters.Grayscale());
img.filters.push(new fabric.Image.filters.Multiply({color: '#F0F'}));
img.scale(0.3);
img.applyFilters(function() {
canvas.add(img);
});
}, {crossOrigin: 'Anonymous'});
fabric.Image.fromURL("http://fabricjs.com/assets/pug.jpg", function(img) {
img.filters.push(new fabric.Image.filters.Luminosity());
img.filters.push(new fabric.Image.filters.Multiply({color: '#F0F'}));
img.applyFilters(function() {
img.scale(0.3);
img.left = img.getWidth();
canvas.add(img);
});
}, {crossOrigin: 'Anonymous'});
<script src="http://www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas width="500" height="400" id="c" ></canvas>
为了将 fabricjs 的内置函数与 gimp 的示例进行比较,我创建了一个亮度过滤器来代替基于 "Average" 方法的灰度过滤器。如您所见,结果非常相似,但它取决于图像。
如果您想构建自己的过滤器,请检查乘法过滤器源代码以了解如何处理过滤器中的参数。
为了能够使用您的过滤器,您需要根据 Fabricjs filter boilerplate
将过滤器代码更改为以下内容
(function(global) {
'use strict';
var fabric = global.fabric || (global.fabric = { }),
filters = fabric.Image.filters,
createClass = fabric.util.createClass;
/**
* Redify filter class
* @class fabric.Image.filters.Redify
* @memberOf fabric.Image.filters
* @extends fabric.Image.filters.BaseFilter
* @see {@link fabric.Image.filters.Redify#initialize} for constructor definition
* @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
* @example
* var filter = new fabric.Image.filters.Redify({
* add here an example of how to use your filter
* });
* object.filters.push(filter);
* object.applyFilters();
*/
filters.Redify = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Redify.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Redify',
/**
* Fragment source for the threshold program
*/
fragmentSource: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uthreshold;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = texture2D(uTexture, vTexCoord);\n' +
// add your gl code here
'gl_FragColor = color;\n' +
'}',
/**
* Redify value, from -1 to 1.
* translated to -255 to 255 for 2d
* 0.0039215686 is the part of 1 that get translated to 1 in 2d
* @param {Number} threshold
* @default
*/
threshold: 5,
/**
* Describe the property that is the filter parameter
* @param {String} m
* @default
*/
mainParameter: 'threshold',
/**
* Apply the Redify 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, i, len = data.length,sublim = 255-this.threshold;
for (i = 0; i < len; i += 4) {
if (data[i] < sublim && data[i + 1] < sublim && data[i + 2] < sublim) {
data[i + 1] = 0;
data[i + 2] = 0;
}
}
},
/**
* 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 {
uMyParameter: gl.getUniformLocation(program, 'uMyParameter'),
};
},
/**
* 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.uniform1f(uniformLocations.uMyParameter, this.threshold);
},
});
/**
* 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.Redify} Instance of fabric.Image.filters.Redify
*/
fabric.Image.filters.Redify.fromObject = fabric.Image.filters.BaseFilter.fromObject;
})(typeof exports !== 'undefined' ? exports : this);
完成后,您可以简单地使用它来传递变量
fabric.Image.fromURL('pug.jpg', function(img) {
img.filters.push(
new fabric.Image.filters.Redify({ threshold: 10 }));
img.applyFilters();
canvas.add(img);
});
我正在尝试创建一个它应该喜欢接受颜色代码的自定义过滤器。
这是我的代码。
工作正常。
fabric.Image.fromURL('pug.jpg', function(img) {
img.filters.push(
new fabric.Image.filters.Sepia(),
new fabric.Image.filters.Brightness({ brightness: 100 }));
img.applyFilters(canvas.renderAll.bind(canvas));
canvas.add(img);
});
现在,我需要创建一个具有特定颜色代码的滤镜。 我发现的是
fabric.Image.filters.Redify = fabric.util.createClass({
type: 'Redify',
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data;
for (var i = 0, len = data.length; i < len; i += 4) {
data[i + 1] = 0;
data[i + 2] = 0;
}
context.putImageData(imageData, 0, 0);
}
});
fabric.Image.filters.Redify.fromObject = function(object) {
return new fabric.Image.filters.Redify(object);
};
我需要解释一下 for 循环的作用...还请解释一下我如何传递颜色代码。
您发现的 redify 滤镜并不是真正的 colorify 滤镜。正如您从代码中看到的那样,它正在杀死绿色和蓝色通道,只留下图像的红色。这与应用红色着色效果不同。
您可以使用相同的方式创建蓝化和绿化滤镜,只需更改幸存的频道即可:
fabric.Image.filters.Greenify= fabric.util.createClass({
type: 'greenify',
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data;
for (var i = 0, len = data.length; i < len; i += 4) {
//kill red
data[i] = 0;
//kill blue
data[i + 2] = 0;
}
context.putImageData(imageData, 0, 0);
}
});
要创建一个彩色滤镜,首先你必须知道如何去做。我亲自检查了 GIMP 的 colorify 滤镜是如何工作的:
https://docs.gimp.org/en/plug-in-colorify.html
1) 根据亮度使图像灰度化
2) 乘以你想要的颜色的灰度级
这或多或少等于按灰度和乘法顺序应用现有的 fabricjs 过滤器。
var canvas = new fabric.Canvas("c");
fabric.Image.filters.Luminosity = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Luminosity.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Luminosity',
/**
* Applies filter to canvas element
* @memberOf fabric.Image.filters.Grayscale.prototype
* @param {Object} canvasEl Canvas element to apply filter to
*/
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data,
len = imageData.width * imageData.height * 4,
index = 0,
average;
while (index < len) {
//Luminosity = 0.21 × R + 0.72 × G + 0.07 × B
average = (0.21 * data[index] + 0.72 * data[index + 1] + 0.07 * data[index + 2]);
data[index] = average;
data[index + 1] = average;
data[index + 2] = average;
index += 4;
}
context.putImageData(imageData, 0, 0);
}
});
/**
* Returns filter instance from an object representation
* @static
* @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
*/
fabric.Image.filters.Grayscale.fromObject = function() {
return new fabric.Image.filters.Grayscale();
};
fabric.Image.fromURL("http://fabricjs.com/assets/pug.jpg", function(img) {
img.filters.push(new fabric.Image.filters.Grayscale());
img.filters.push(new fabric.Image.filters.Multiply({color: '#F0F'}));
img.scale(0.3);
img.applyFilters(function() {
canvas.add(img);
});
}, {crossOrigin: 'Anonymous'});
fabric.Image.fromURL("http://fabricjs.com/assets/pug.jpg", function(img) {
img.filters.push(new fabric.Image.filters.Luminosity());
img.filters.push(new fabric.Image.filters.Multiply({color: '#F0F'}));
img.applyFilters(function() {
img.scale(0.3);
img.left = img.getWidth();
canvas.add(img);
});
}, {crossOrigin: 'Anonymous'});
<script src="http://www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas width="500" height="400" id="c" ></canvas>
如果您想构建自己的过滤器,请检查乘法过滤器源代码以了解如何处理过滤器中的参数。
为了能够使用您的过滤器,您需要根据 Fabricjs filter boilerplate
将过滤器代码更改为以下内容(function(global) {
'use strict';
var fabric = global.fabric || (global.fabric = { }),
filters = fabric.Image.filters,
createClass = fabric.util.createClass;
/**
* Redify filter class
* @class fabric.Image.filters.Redify
* @memberOf fabric.Image.filters
* @extends fabric.Image.filters.BaseFilter
* @see {@link fabric.Image.filters.Redify#initialize} for constructor definition
* @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
* @example
* var filter = new fabric.Image.filters.Redify({
* add here an example of how to use your filter
* });
* object.filters.push(filter);
* object.applyFilters();
*/
filters.Redify = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Redify.prototype */ {
/**
* Filter type
* @param {String} type
* @default
*/
type: 'Redify',
/**
* Fragment source for the threshold program
*/
fragmentSource: 'precision highp float;\n' +
'uniform sampler2D uTexture;\n' +
'uniform float uthreshold;\n' +
'varying vec2 vTexCoord;\n' +
'void main() {\n' +
'vec4 color = texture2D(uTexture, vTexCoord);\n' +
// add your gl code here
'gl_FragColor = color;\n' +
'}',
/**
* Redify value, from -1 to 1.
* translated to -255 to 255 for 2d
* 0.0039215686 is the part of 1 that get translated to 1 in 2d
* @param {Number} threshold
* @default
*/
threshold: 5,
/**
* Describe the property that is the filter parameter
* @param {String} m
* @default
*/
mainParameter: 'threshold',
/**
* Apply the Redify 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, i, len = data.length,sublim = 255-this.threshold;
for (i = 0; i < len; i += 4) {
if (data[i] < sublim && data[i + 1] < sublim && data[i + 2] < sublim) {
data[i + 1] = 0;
data[i + 2] = 0;
}
}
},
/**
* 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 {
uMyParameter: gl.getUniformLocation(program, 'uMyParameter'),
};
},
/**
* 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.uniform1f(uniformLocations.uMyParameter, this.threshold);
},
});
/**
* 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.Redify} Instance of fabric.Image.filters.Redify
*/
fabric.Image.filters.Redify.fromObject = fabric.Image.filters.BaseFilter.fromObject;
})(typeof exports !== 'undefined' ? exports : this);
完成后,您可以简单地使用它来传递变量
fabric.Image.fromURL('pug.jpg', function(img) {
img.filters.push(
new fabric.Image.filters.Redify({ threshold: 10 }));
img.applyFilters();
canvas.add(img);
});