refactor: change Filter input and output

This commit is contained in:
quark 2024-08-19 09:57:31 +08:00
parent 583fccf249
commit de580be9b5
21 changed files with 239 additions and 81 deletions

View File

@ -15,7 +15,8 @@ import { Context } from './Context';
import { Shape } from './Shape';
import { Layer } from './Layer';
export type Filter = (this: Node, imageData: ImageData) => void;
export type LegalCanvas = HTMLCanvasElement | OffscreenCanvas;
export type Filter = (this: Node, canvas: LegalCanvas) => void;
type globalCompositeOperationType =
| ''
@ -163,7 +164,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
_dragEventId: number | null = null;
_shouldFireChangeEvents = false;
constructor(config?: Config) {
constructor (config?: Config) {
// on initial set attrs wi don't need to fire change events
// because nobody is listening to them yet
this.setAttrs(config);
@ -366,10 +367,10 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
// console.log({ x, y, width, height }, rect);
var cachedSceneCanvas = new SceneCanvas({
pixelRatio: pixelRatio,
width: width,
height: height,
}),
pixelRatio: pixelRatio,
width: width,
height: height,
}),
cachedFilterCanvas = new SceneCanvas({
pixelRatio: pixelRatio,
width: 0,
@ -591,12 +592,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
sceneCanvas.getWidth() / ratio,
sceneCanvas.getHeight() / ratio
);
imageData = filterContext.getImageData(
0,
0,
filterCanvas.getWidth(),
filterCanvas.getHeight()
);
let canvas = Util.createOffScreenCanvas(filterCanvas.getWidth(), filterCanvas.getHeight());
// apply filters to filter context
for (n = 0; n < len; n++) {
@ -604,19 +600,20 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
if (typeof filter !== 'function') {
Util.error(
'Filter should be type of function, but got ' +
typeof filter +
' instead. Please check correct filters'
typeof filter +
' instead. Please check correct filters'
);
continue;
}
filter.call(this, imageData);
filterContext.putImageData(imageData, 0, 0);
canvas = filter.call(this, canvas);
}
// draw the final result on top of scene canvas
filterContext.drawImage(canvas, 0, 0);
} catch (e: any) {
Util.error(
'Unable to apply filter. ' +
e.message +
' This post my help you https://konvajs.org/docs/posts/Tainted_Canvas.html.'
e.message +
' This post my help you https://konvajs.org/docs/posts/Tainted_Canvas.html.'
);
}
@ -1435,10 +1432,10 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
if (zIndex < 0 || zIndex >= this.parent.children.length) {
Util.warn(
'Unexpected value ' +
zIndex +
' for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to ' +
(this.parent.children.length - 1) +
'.'
zIndex +
' for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to ' +
(this.parent.children.length - 1) +
'.'
);
}
var index = this.index;
@ -1619,8 +1616,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
if (!Util.isValidSelector(sel)) {
Util.warn(
'Selector "' +
sel +
'" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".'
sel +
'" is invalid. Allowed selectors examples are "#foo", ".bar" or "Group".'
);
Util.warn(
'If you have a custom shape with such className, please change it to start with upper letter like "Triangle".'
@ -2714,8 +2711,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
if (!Konva[className]) {
Util.warn(
'Can not find a node with class name "' +
className +
'". Fallback to "Shape".'
className +
'". Fallback to "Shape".'
);
className = 'Shape';
}

View File

@ -29,7 +29,7 @@ import { IRect, RGB, RGBA, Vector2d } from './types';
export class Transform {
m: Array<number>;
dirty = false;
constructor(m = [1, 0, 0, 1, 0, 0]) {
constructor (m = [1, 0, 0, 1, 0, 0]) {
this.m = (m && m.slice()) || [1, 0, 0, 1, 0, 0];
}
reset() {
@ -509,9 +509,18 @@ export const Util = {
// on some environments canvas.style is readonly
try {
(<any>canvas).style = canvas.style || {};
} catch (e) {}
} catch (e) { }
return canvas;
},
createOffScreenCanvas(size: { width: number; height: number }) {
if (window.OffscreenCanvas !== undefined) {
return new OffscreenCanvas(size.width, size.height);
}
const canvas = this.createCanvasElement();
canvas.width = size.width;
canvas.height = size.height;
return canvas;
}
createImageElement() {
return document.createElement('img');
},
@ -1005,7 +1014,7 @@ export const Util = {
topRight =
bottomLeft =
bottomRight =
Math.min(cornerRadius, width / 2, height / 2);
Math.min(cornerRadius, width / 2, height / 2);
} else {
topLeft = Math.min(cornerRadius[0] || 0, width / 2, height / 2);
topRight = Math.min(cornerRadius[1] || 0, width / 2, height / 2);

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
/*
the Gauss filter
@ -154,7 +154,7 @@ function filterGaussBlurRGBA(imageData, radius) {
g_sum =
b_sum =
a_sum =
0;
0;
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
@ -254,7 +254,7 @@ function filterGaussBlurRGBA(imageData, radius) {
b_sum =
a_sum =
r_sum =
0;
0;
yi = x << 2;
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
@ -358,15 +358,20 @@ function filterGaussBlurRGBA(imageData, radius) {
* @function
* @name Blur
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Blur]);
* node.blurRadius(10);
*/
export const Blur: Filter = function Blur(imageData) {
export const Blur: Filter = function Blur(canvas: LegalCanvas) {
var radius = Math.round(this.blurRadius());
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
if (radius > 0) {
filterGaussBlurRGBA(imageData, radius);
}

View File

@ -1,18 +1,24 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
/**
* Brighten Filter.
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Brighten]);
* node.brightness(0.8);
*/
export const Brighten: Filter = function (imageData) {
export const Brighten: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var brightness = this.brightness() * 255,
data = imageData.data,
len = data.length,
@ -26,6 +32,9 @@ export const Brighten: Filter = function (imageData) {
// blue
data[i + 2] += brightness;
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -1,18 +1,24 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
/**
* Contrast Filter.
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Contrast]);
* node.contrast(10);
*/
export const Contrast: Filter = function (imageData) {
export const Contrast: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var adjust = Math.pow((this.contrast() + 100) / 100, 2);
var data = imageData.data,
@ -56,6 +62,9 @@ export const Contrast: Filter = function (imageData) {
data[i + 1] = green;
data[i + 2] = blue;
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
/**

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { Util } from '../Util';
import { getNumberValidator } from '../Validators';
/**
@ -9,7 +9,7 @@ import { getNumberValidator } from '../Validators';
* License: [http://www.pixastic.com/lib/license.txt]
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Emboss]);
@ -18,11 +18,17 @@ import { getNumberValidator } from '../Validators';
* node.embossDirection('right');
* node.embossBlend(true);
*/
export const Emboss: Filter = function (imageData) {
export const Emboss: Filter = function (canvas: LegalCanvas) {
// pixastic strength is between 0 and 10. I want it between 0 and 1
// pixastic greyLevel is between 0 and 255. I want it between 0 and 1. Also,
// a max value of greyLevel yields a white emboss, and the min value yields a black
// emboss. Therefore, I changed greyLevel to whiteLevel
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var strength = this.embossStrength() * 10,
greyLevel = this.embossWhiteLevel() * 255,
direction = this.embossDirection(),
@ -138,6 +144,9 @@ export const Emboss: Filter = function (imageData) {
}
} while (--x);
} while (--y);
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
function remap(fromValue, fromMin, fromMax, toMin, toMax) {
@ -30,14 +30,20 @@ function remap(fromValue, fromMin, fromMax, toMin, toMax) {
* @function
* @name Enhance
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* node.cache();
* node.filters([Konva.Filters.Enhance]);
* node.enhance(0.4);
*/
export const Enhance: Filter = function (imageData) {
export const Enhance: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nSubPixels = data.length,
rMin = data[0],
@ -134,6 +140,8 @@ export const Enhance: Filter = function (imageData) {
data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
//data[i + 3] = remap(data[i + 3], aMin, aMax, aGoalMin, aGoalMax);
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
/**

View File

@ -1,15 +1,21 @@
import { Filter } from '../Node';
import { Filter, LegalCanvas } from '../Node';
/**
* Grayscale Filter
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Grayscale]);
*/
export const Grayscale: Filter = function (imageData) {
export const Grayscale: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
len = data.length,
i,
@ -24,4 +30,6 @@ export const Grayscale: Filter = function (imageData) {
// blue
data[i + 2] = brightness;
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
Factory.addGetterSetter(
@ -51,14 +51,20 @@ Factory.addGetterSetter(
* HSL Filter. Adjusts the hue, saturation and luminance (or lightness)
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* image.filters([Konva.Filters.HSL]);
* image.luminance(0.2);
*/
export const HSL: Filter = function (imageData) {
export const HSL: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nPixels = data.length,
v = 1,
@ -105,4 +111,6 @@ export const HSL: Filter = function (imageData) {
data[i + 2] = br * r + bg * g + bb * b + l;
data[i + 3] = a; // alpha
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};

View File

@ -7,14 +7,20 @@ import { getNumberValidator } from '../Validators';
* @function
* @name HSV
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* image.filters([Konva.Filters.HSV]);
* image.value(200);
*/
export const HSV: Filter = function (imageData) {
export const HSV: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nPixels = data.length,
v = Math.pow(2, this.value()),
@ -60,6 +66,8 @@ export const HSV: Filter = function (imageData) {
data[i + 2] = br * r + bg * g + bb * b;
data[i + 3] = a; // alpha
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -3,12 +3,18 @@ import { Filter } from '../Node';
* Invert Filter
* @function
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Invert]);
*/
export const Invert: Filter = function (imageData) {
export const Invert: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
len = data.length,
i;
@ -21,4 +27,6 @@ export const Invert: Filter = function (imageData) {
// blue
data[i + 2] = 255 - data[i + 2];
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};

View File

@ -177,7 +177,13 @@ var FromPolar = function (src, dst, opt) {
* node.kaleidoscopePower(3);
* node.kaleidoscopeAngle(45);
*/
export const Kaleidoscope: Filter = function (imageData) {
export const Kaleidoscope: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var xSize = imageData.width,
ySize = imageData.height;
@ -263,6 +269,8 @@ export const Kaleidoscope: Filter = function (imageData) {
// Convert back from polar coordinates
FromPolar(scratchData, imageData, { polarRotation: 0 });
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
/**

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
function pixelAt(idata, x, y) {
@ -17,8 +17,8 @@ function pixelAt(idata, x, y) {
function rgbDistance(p1, p2) {
return Math.sqrt(
Math.pow(p1[0] - p2[0], 2) +
Math.pow(p1[1] - p2[1], 2) +
Math.pow(p1[2] - p2[2], 2)
Math.pow(p1[1] - p2[1], 2) +
Math.pow(p1[2] - p2[2], 2)
);
}
@ -173,13 +173,19 @@ function smoothEdgeMask(mask, sw, sh) {
* @function
* @name Mask
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Mask]);
* node.threshold(200);
*/
export const Mask: Filter = function (imageData) {
export const Mask: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
// Detect pixels close to the background color
var threshold = this.threshold(),
mask = backgroundMask(imageData, threshold);
@ -197,7 +203,8 @@ export const Mask: Filter = function (imageData) {
applyMask(imageData, mask);
}
return imageData;
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -7,14 +7,20 @@ import { getNumberValidator } from '../Validators';
* @function
* @name Noise
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* node.cache();
* node.filters([Konva.Filters.Noise]);
* node.noise(0.8);
*/
export const Noise: Filter = function (imageData) {
export const Noise: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var amount = this.noise() * 255,
data = imageData.data,
nPixels = data.length,
@ -26,6 +32,8 @@ export const Noise: Filter = function (imageData) {
data[i + 1] += half - 2 * half * Math.random();
data[i + 2] += half - 2 * half * Math.random();
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -10,7 +10,7 @@ import { getNumberValidator } from '../Validators';
* @function
* @name Pixelate
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* node.cache();
@ -18,7 +18,13 @@ import { getNumberValidator } from '../Validators';
* node.pixelSize(10);
*/
export const Pixelate: Filter = function (imageData) {
export const Pixelate: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var pixelSize = Math.ceil(this.pixelSize()),
width = imageData.width,
height = imageData.height,
@ -103,6 +109,9 @@ export const Pixelate: Filter = function (imageData) {
}
}
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -1,5 +1,5 @@
import { Factory } from '../Factory';
import { Node, Filter } from '../Node';
import { Node, Filter, LegalCanvas } from '../Node';
import { getNumberValidator } from '../Validators';
/**
* Posterize Filter. Adjusts the channels so that there are no more
@ -9,14 +9,20 @@ import { getNumberValidator } from '../Validators';
* @name Posterize
* @author ippo615
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Posterize]);
* node.levels(0.8); // between 0 and 1
*/
export const Posterize: Filter = function (imageData) {
export const Posterize: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
// level must be between 1 and 255
var levels = Math.round(this.levels() * 254) + 1,
data = imageData.data,
@ -27,6 +33,8 @@ export const Posterize: Filter = function (imageData) {
for (i = 0; i < len; i += 1) {
data[i] = Math.floor(data[i] / scale) * scale;
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(

View File

@ -7,7 +7,7 @@ import { RGBComponent } from '../Validators';
* @function
* @name RGB
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* node.cache();
@ -16,7 +16,13 @@ import { RGBComponent } from '../Validators';
* node.green(200);
*/
export const RGB: Filter = function (imageData) {
export const RGB: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nPixels = data.length,
red = this.red(),
@ -33,6 +39,8 @@ export const RGB: Filter = function (imageData) {
data[i + 2] = brightness * blue; // b
data[i + 3] = data[i + 3]; // alpha
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(Node, 'red', 0, function (this: Node, val) {

View File

@ -7,7 +7,7 @@ import { RGBComponent } from '../Validators';
* @function
* @name RGBA
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author codefo
* @example
* node.cache();
@ -17,7 +17,13 @@ import { RGBComponent } from '../Validators';
* node.alpha(0.3);
*/
export const RGBA: Filter = function (imageData) {
export const RGBA: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nPixels = data.length,
red = this.red(),
@ -34,6 +40,8 @@ export const RGBA: Filter = function (imageData) {
data[i + 1] = green * alpha + data[i + 1] * ia; // g
data[i + 2] = blue * alpha + data[i + 2] * ia; // b
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(Node, 'red', 0, function (this: Node, val: number) {

View File

@ -6,12 +6,18 @@ import { Filter } from '../Node';
* @function
* @name Sepia
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Sepia]);
*/
export const Sepia: Filter = function (imageData) {
export const Sepia: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
nPixels = data.length,
i,
@ -28,4 +34,6 @@ export const Sepia: Filter = function (imageData) {
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};

View File

@ -7,13 +7,19 @@ import { Filter } from '../Node';
* @function
* @name Solarize
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @example
* node.cache();
* node.filters([Konva.Filters.Solarize]);
*/
export const Solarize: Filter = function (imageData) {
export const Solarize: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var data = imageData.data,
w = imageData.width,
h = imageData.height,
@ -44,4 +50,6 @@ export const Solarize: Filter = function (imageData) {
data[offset + 2] = b;
} while (--x);
} while (--y);
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};

View File

@ -8,7 +8,7 @@ import { getNumberValidator } from '../Validators';
* @function
* @name Threshold
* @memberof Konva.Filters
* @param {Object} imageData
* * @param {LegalCanvas} canvas
* @author ippo615
* @example
* node.cache();
@ -16,7 +16,13 @@ import { getNumberValidator } from '../Validators';
* node.threshold(0.1);
*/
export const Threshold: Filter = function (imageData) {
export const Threshold: Filter = function (canvas: LegalCanvas) {
const imageData = canvas.context.getImageData(
0,
0,
canvas.width,
canvas.height
);
var level = this.threshold() * 255,
data = imageData.data,
len = data.length,
@ -25,6 +31,8 @@ export const Threshold: Filter = function (imageData) {
for (i = 0; i < len; i += 1) {
data[i] = data[i] < level ? 0 : 255;
}
canvas.context.putImageData(imageData, 0, 0);
return canvas;
};
Factory.addGetterSetter(