FabricJS Filter Halftone Color

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;

uniform float uDensity;      // For imperfect, isotropic anti-aliasing in
uniform float uFrequency;

// Anti-aliased step function. If the auto derivatives extension
// is supported, the AA is done in a fully general, anisotropic
// manner. If not, the expression for "afwidth" is a kludge for
// this particular shader and this particular view transform.
float aastep(float threshold, float value) {
  float afwidth = uFrequency * (1.0/200.0) / uDensity;
  return smoothstep(threshold-afwidth, threshold+afwidth, value);
}
 
// Explicit bilinear texture lookup to circumvent bad hardware precision.
// The extra arguments specify the dimension of the texture. (GLSL 1.30
// introduced textureSize() to get that information from the sampler.)
// 'dims' is the width and height of the texture, 'one' is 1.0/dims.
// (Precomputing 'one' saves two divisions for each lookup.)
vec4 texture2D_bilinear(sampler2D tex, vec2 st, vec2 dims, vec2 one) {
  vec2 uv = st * dims;
  vec2 uv00 = floor(uv - vec2(0.5)); // Lower left corner of lower left texel
  vec2 uvlerp = uv - uv00 - vec2(0.5); // Texel-local lerp blends [0,1]
  vec2 st00 = (uv00 + vec2(0.5)) * one;
  vec4 texel00 = texture2D(tex, st00);
  vec4 texel10 = texture2D(tex, st00 + vec2(one.x, 0.0));
  vec4 texel01 = texture2D(tex, st00 + vec2(0.0, one.y));
  vec4 texel11 = texture2D(tex, st00 + one);
  vec4 texel0 = mix(texel00, texel01, uvlerp.y); 
  vec4 texel1 = mix(texel10, texel11, uvlerp.y); 
  return mix(texel0, texel1, uvlerp.x);
}
 
// 2D simplex noise
 
// Description : Array- and textureless GLSL 2D simplex noise.
// Author : Ian McEwan, Ashima Arts. Version: 20110822
// Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed...

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.HalftoneCMYK = fabric.util.createClass(fabric.Image.filters.BaseFilter, {

  type: 'HalftoneCMYK',
	density:1,
  frequency:1,
  mainParameter:"density",
  /**
   * 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 {
        uDensity: gl.getUniformLocation(program, 'uDensity'),
        uFrequency: gl.getUniformLocation(program, 'uFrequency')
      };
    },
  sendUniformData: function(gl, uniformLocations) {
      gl.uniform1f(uniformLocations.uDensity, this.density);
      gl.uniform1f(uniformLocations.uFrequency, this.frequency);
    }
});

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

var halftone = new fabric.Image.filters.HalftoneCMYK({density:0.7,frequency:100});
imgInstance.filters.push(halftone);
imgInstance.applyFilters();

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


slider = window.document.getElementById("frequency");
slider.oninput = slider.onchange = function(){
	halftone.frequency = this.value;
  console.log(this.value);
  imgInstance.applyFilters();
 ...