如何通过在 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);
});