FabricJS Filter Halftone

by codingdude

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
<script id="halftoneShader" type="x-shader/x-vertex">
precision highp float;
uniform sampler2D uTexture;
uniform float uPixelsPerRow;
uniform float uStepW,uStepH;
varying vec2 vTexCoord;

void main() {
	vec2 p = vTexCoord.st;
	float pixelSize = 1.0 / uPixelsPerRow;
	
	float dx = mod(p.x, pixelSize) - pixelSize*0.5;
	float dy = mod(p.y, pixelSize) - pixelSize*0.5;
	
	p.x -= dx;
	p.y -= dy;
	vec3 col = texture2D(uTexture, p).rgb;
	float bright = 0.3333*(col.r+col.g+col.b);
	
	float dist = sqrt(dx*dx + dy*dy);
	float rad = bright * pixelSize * 0.8;
	float m = step(dist, rad);

	vec3 col2 = mix(vec3(0.0), vec3(1.0), m);
	gl_FragColor = vec4(col2, 1.0);
}
</script>


<canvas width="600" height="400" id="c"></canvas>
<img style="display:none" src="https://upload.wikimedia.org/wikipedia/commons/f/f9/Phoenicopterus_ruber_in_S%C3%A3o_Paulo_Zoo.jpg" id="my-image" crossorigin="anonymous">
<br/>
Phase:
<input type="range" min="1" max="600" step="0.01" id="pixelsPerRow"/>

JavaScript

//https://github.com/genekogan/Processing-Shader-Examples/blob/master/TextureShaders/data/
fabric.textureSize = 4096;//for filters on large images
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-image');
var imgInstance = new fabric.Image(imgElement, {
  left: 100,
  top: 50,
  angle: 0,
  opacity: 1,
  scaleX:0.1,
  scaleY:0.1
});
canvas.add(imgInstance);



fabric.Image.filters.Halftone = fabric.util.createClass(fabric.Image.filters.BaseFilter, {

  type: 'Halftone',
	radius:0.1,
  smoothness:0.5,
  mainParameter:"pixelsPerRow",
  /**
   * Fragment source for the redify program
   */
  fragmentSource: window.document.getElementById("halftoneShader").textContent,

  applyTo2d: function(options) {
    var imageData = options.imageData,
        data = imageData.data, i, len = data.length;

    for (i = 0; i < len; i += 4) {
      data[i + 1] = 0;
      data[i + 2] = 0;
    }

  },
  getUniformLocations: function(gl, program) {
      return {
        uPixelsPerRow: gl.getUniformLocation(program, 'uPixelsPerRow')
      };
    },
  sendUniformData: function(gl, uniformLocations) {
      gl.uniform1f(uniformLocations.uPixelsPerRow, this.pixelsPerRow);
    }
});

fabric.Image.filters.Halftone.fromObject = fabric.Image.filters.BaseFilter.fromObject;

var halftone = new fabric.Image.filters.Halftone({pixelsPerRow:600});
imgInstance.filters.push(halftone);
imgInstance.applyFilters();

var slider = window.document.getElementById("pixelsPerRow");
slider.oninput = slider.onchange = function(){
	halftone.pixelsPerRow = this.value;
  console.log(this.value);
  imgInstance.applyFilters();
  canvas.requestRenderAll();
}