Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
236 views
in Technique[技术] by (71.8m points)

javascript - Fabric.js manipulate Image object pixel by pixel to change color

I want to get the data of an image of a fabric.Image object to loop through each pixel and change its color if it's a given color to another given color. But I don't want to get the data of the entire canvas, only of that specific object. Something like this -

changeColor = (object, targetColor, replacementColor) => {
  data = object.getData();

  for(var i = 0, n = data.length; i<n; i+=4){
    let r = data[i];
    let g = data[i + 1];
    let b = data[i + 2];
    
    if(r === targetColor.r && g === targetColor.g && b === targetColor.b){
      imgd[i] = replacementColor.r;
      imgd[i + 1] = replacementColor.g;
      imgd[i + 2] = replacementColor.b;
      imgd[i + 3] = replacementColor.a;
    }
  }
}

But I do not know how to get the data of an object (I know getData() is not a real function).

TL;DR: I want a way to get the data of an image of a Fabric object (not the entire canvas) and then be able to manipulate its pixels and put the modified image back in.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The best way to do this is with an image filter, I'd say. Fortunately, Fabric provides an image filter with a very similar functionality - RemoveColor (http://fabricjs.com/docs/fabric.js.html#line23120). We can take that implementation, tweak it a little to replace with a configurable color instead of removing, and that should do the trick.

There are three main changes we make to the RemoveColor class to create our ReplaceColor class:

  • Adding a replacementColor field that takes a 4 wide array of the color to replace with
  • Modifying the webGL to take in an extra parameter for the replacement color:
        fragmentSource: 'precision highp float;
' +
            'uniform sampler2D uTexture;
' +
            'uniform vec4 uLow;
' +
            'uniform vec4 uHigh;
' +
            'uniform vec4 uRep;
' + // New variable for replacement color
            'varying vec2 vTexCoord;
' +
            'void main() {
' +
            'gl_FragColor = texture2D(uTexture, vTexCoord);
' +
            'if(all(greaterThan(gl_FragColor.rgb,uLow.rgb)) && all(greaterThan(uHigh.rgb,gl_FragColor.rgb))) {
' +
            'gl_FragColor.rgb = uRep.rgb;
' + // Here we set the color instead of 0-ing out the alpha
            'gl_FragColor.a = uRep.a;
' +
            '}
' +
            '}',
  • Updating the applyTo2d method to also use the replacement color:
        applyTo2d: function (options) {
            var imageData = options.imageData,
                data = imageData.data, i,
                distance = this.distance * 255,
                r, g, b,
                source = new fabric.Color(this.color).getSource(),
                lowC = [
                    source[0] - distance,
                    source[1] - distance,
                    source[2] - distance,
                ],
                highC = [
                    source[0] + distance,
                    source[1] + distance,
                    source[2] + distance,
                ];

            for (i = 0; i < data.length; i += 4) {
                r = data[i];
                g = data[i + 1];
                b = data[i + 2];
                if (r > lowC[0] &&
                    g > lowC[1] &&
                    b > lowC[2] &&
                    r < highC[0] &&
                    g < highC[1] &&
                    b < highC[2]) {
                    data[i] = this.replacementColor[0]; // Here we also modify the color directly instead of 0-ing out the alpha
                    data[i + 1] = this.replacementColor[1];
                    data[i + 2] = this.replacementColor[2];
                    data[i + 3] = this.replacementColor[3];
                }
            }
        }

JSFiddle: https://jsfiddle.net/8coka6yv/

Note that on the JSFiddle I cranked up the distance field, so it's a pretty drastic modification. The default value is normally 0.02.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...