Skip to content

Commit

Permalink
chore(TS): Converting the filtering backend to ts + es6 (#8403)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored Nov 19, 2022
1 parent 71d4d3b commit 46f4759
Show file tree
Hide file tree
Showing 8 changed files with 935 additions and 848 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- chore(TS): migrate filter backends [#8403](https://github.com/fabricjs/fabric.js/pull/8403)
- chore(TS): migrate Text classes/mixins [#8408](https://github.com/fabricjs/fabric.js/pull/8408)
- chore(TS): migrate Path [#8412](https://github.com/fabricjs/fabric.js/pull/8412)
- ci(): remove unwanted build stats (from [#8395](https://github.com/fabricjs/fabric.js/pull/8395)) [#8416](https://github.com/fabricjs/fabric.js/pull/8416)
Expand Down
3 changes: 1 addition & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ import './src/shapes/group.class';
import './src/shapes/active_selection.class'; // optional interaction
import './src/shapes/image.class';
import './src/mixins/object_straightening.mixin'; // optional objectstraightening
import './src/filters/webgl_backend.class'; // optional image_filters
import './src/filters/2d_backend.class'; // optional image_filters
import './src/filters/WebGLProbe'; // optional image_filters
import './src/filters/base_filter.class'; // optional image_filters
import './src/filters/colormatrix_filter.class'; // optional image_filters
import './src/filters/brightness_filter.class'; // optional image_filters
Expand Down
132 changes: 59 additions & 73 deletions src/filters/2d_backend.class.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,64 @@
//@ts-nocheck
(function (global) {
var fabric = global.fabric,
noop = function () {};
/**
* Canvas 2D filter backend.
*/
import { T2DPipelineState } from './typedefs';

fabric.Canvas2dFilterBackend = Canvas2dFilterBackend;
export class Canvas2dFilterBackend {
/**
* Experimental. This object is a sort of repository of help layers used to avoid
* of recreating them during frequent filtering. If you are previewing a filter with
* a slider you probably do not want to create help layers every filter step.
* in this object there will be appended some canvases, created once, resized sometimes
* cleared never. Clearing is left to the developer.
**/
resources = {};

/**
* Canvas 2D filter backend.
* Apply a set of filters against a source image and draw the filtered output
* to the provided destination canvas.
*
* @param {EnhancedFilter} filters The filter to apply.
* @param {HTMLImageElement|HTMLCanvasElement} sourceElement The source to be filtered.
* @param {Number} sourceWidth The width of the source input.
* @param {Number} sourceHeight The height of the source input.
* @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.
*/
function Canvas2dFilterBackend() {}

Canvas2dFilterBackend.prototype =
/** @lends fabric.Canvas2dFilterBackend.prototype */ {
evictCachesForKey: noop,
dispose: noop,
clearWebGLCaches: noop,

/**
* Experimental. This object is a sort of repository of help layers used to avoid
* of recreating them during frequent filtering. If you are previewing a filter with
* a slider you probably do not want to create help layers every filter step.
* in this object there will be appended some canvases, created once, resized sometimes
* cleared never. Clearing is left to the developer.
**/
resources: {},

/**
* Apply a set of filters against a source image and draw the filtered output
* to the provided destination canvas.
*
* @param {EnhancedFilter} filters The filter to apply.
* @param {HTMLImageElement|HTMLCanvasElement} sourceElement The source to be filtered.
* @param {Number} sourceWidth The width of the source input.
* @param {Number} sourceHeight The height of the source input.
* @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.
*/
applyFilters: function (
filters,
sourceElement,
sourceWidth,
sourceHeight,
targetCanvas
) {
var ctx = targetCanvas.getContext('2d');
ctx.drawImage(sourceElement, 0, 0, sourceWidth, sourceHeight);
var imageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight);
var originalImageData = ctx.getImageData(
0,
0,
sourceWidth,
sourceHeight
);
var pipelineState = {
sourceWidth: sourceWidth,
sourceHeight: sourceHeight,
imageData: imageData,
originalEl: sourceElement,
originalImageData: originalImageData,
canvasEl: targetCanvas,
ctx: ctx,
filterBackend: this,
};
filters.forEach(function (filter) {
filter.applyTo(pipelineState);
});
if (
pipelineState.imageData.width !== sourceWidth ||
pipelineState.imageData.height !== sourceHeight
) {
targetCanvas.width = pipelineState.imageData.width;
targetCanvas.height = pipelineState.imageData.height;
}
ctx.putImageData(pipelineState.imageData, 0, 0);
return pipelineState;
},
applyFilters(
filters: any[],
sourceElement: HTMLImageElement | HTMLCanvasElement,
sourceWidth: number,
sourceHeight: number,
targetCanvas: HTMLCanvasElement
): T2DPipelineState | void {
const ctx = targetCanvas.getContext('2d');
if (!ctx) {
return;
}
ctx.drawImage(sourceElement, 0, 0, sourceWidth, sourceHeight);
const imageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight);
const originalImageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight);
const pipelineState: T2DPipelineState = {
sourceWidth,
sourceHeight,
imageData,
originalEl: sourceElement,
originalImageData,
canvasEl: targetCanvas,
ctx,
filterBackend: this,
};
})(typeof exports !== 'undefined' ? exports : window);
filters.forEach(function (filter) {
filter.applyTo(pipelineState);
});
const { imageData: imageDataPostFilter } = pipelineState;
if (
imageDataPostFilter.width !== sourceWidth ||
imageDataPostFilter.height !== sourceHeight
) {
targetCanvas.width = imageDataPostFilter.width;
targetCanvas.height = imageDataPostFilter.height;
}
ctx.putImageData(imageDataPostFilter, 0, 0);
return pipelineState;
}
}
74 changes: 36 additions & 38 deletions src/filters/WebGLProbe.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,38 @@
//@ts-nocheck

import { fabric } from '../../HEADER';
import { config } from '../config';
import { createCanvasElement } from '../util/misc/dom';
import { Canvas2dFilterBackend } from './2d_backend.class';
import { WebGLFilterBackend } from './webgl_backend.class';

export const enum TWebGLPrecision {
export enum WebGLPrecision {
low = 'lowp',
medium = 'mediump',
high = 'highp',
}

/**
* @todo remove once rollup supports transforming enums...
* https://github.com/rollup/plugins/issues/463
*/
const WebGLPrecision = [
TWebGLPrecision.low,
TWebGLPrecision.medium,
TWebGLPrecision.high,
];

/**
* Lazy initialize WebGL contants
*/
class WebGLProbe {
private initialized = false;

private _maxTextureSize?: number;

private _webGLPrecision?: TWebGLPrecision;

get maxTextureSize() {
this.queryWebGL();
return this._maxTextureSize;
}
maxTextureSize?: number;

get webGLPrecision() {
this.queryWebGL();
return this._webGLPrecision;
}
webGLPrecision: WebGLPrecision | undefined;

/**
* Tests if webgl supports certain precision
* @param {WebGL} Canvas WebGL context to test on
* @param {TWebGLPrecision} Precision to test can be any of following
* @param {WebGLPrecision} Precision to test can be any of following
* @returns {Boolean} Whether the user's browser WebGL supports given precision.
*/
private testPrecision(gl: WebGLRenderingContext, precision: TWebGLPrecision) {
private testPrecision(
gl: WebGLRenderingContext,
precision: WebGLPrecision
): boolean {
const fragmentSource = `precision ${precision} float;\nvoid main(){}`;
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
return false;
}
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
return !!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
Expand All @@ -57,21 +42,19 @@ class WebGLProbe {
* query browser for WebGL
* @returns config object if true
*/
private queryWebGL() {
if (this.initialized || fabric.isLikelyNode) {
queryWebGL() {
if (fabric.isLikelyNode) {
return;
}
const canvas = createCanvasElement();
const gl =
canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
const gl = canvas.getContext('webgl');
if (gl) {
this._maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
this._webGLPrecision = WebGLPrecision.find((key) =>
this.testPrecision(gl, key)
this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
this.webGLPrecision = Object.values(WebGLPrecision).find((precision) =>
this.testPrecision(gl, precision)
);
console.log(`fabric: max texture size ${this._maxTextureSize}`);
console.log(`fabric: max texture size ${this.maxTextureSize}`);
}
this.initialized = true;
}

isSupported(textureSize: number) {
Expand All @@ -80,3 +63,18 @@ class WebGLProbe {
}

export const webGLProbe = new WebGLProbe();

export function initFilterBackend():
| WebGLFilterBackend
| Canvas2dFilterBackend {
webGLProbe.queryWebGL();
if (config.enableGLFiltering && webGLProbe.isSupported(config.textureSize)) {
return new WebGLFilterBackend({ tileSize: config.textureSize });
} else {
return new Canvas2dFilterBackend();
}
}

fabric.Canvas2dFilterBackend = Canvas2dFilterBackend;
fabric.WebglFilterBackend = WebGLFilterBackend;
fabric.initFilterBackend = initFilterBackend;
Loading

0 comments on commit 46f4759

Please sign in to comment.