Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(TS): Converting the filtering backend to ts + es6 #8403

Merged
merged 21 commits into from
Nov 19, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 Ellipse [#8408](https://github.com/fabricjs/fabric.js/pull/8408)
- chore(TS): migrate Triangle to TS [#8410](https://github.com/fabricjs/fabric.js/pull/8410)
- chore(TS): migrate Circle to TS [#8406](https://github.com/fabricjs/fabric.js/pull/8406)
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
140 changes: 67 additions & 73 deletions src/filters/2d_backend.class.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,72 @@
//@ts-nocheck
(function (global) {
var fabric = global.fabric,
noop = function () {};
/**
* Canvas 2D filter backend.
*/
import { noop } from '../constants';
import { T2DPipelineState } from './typedefs';

fabric.Canvas2dFilterBackend = Canvas2dFilterBackend;
export class Canvas2dFilterBackend {
evictCachesForKey = noop;
dispose = noop;
clearWebGLCaches = noop;

/**
* Canvas 2D filter backend.
*/
function Canvas2dFilterBackend() {}

Canvas2dFilterBackend.prototype =
/** @lends fabric.Canvas2dFilterBackend.prototype */ {
evictCachesForKey: noop,
dispose: noop,
clearWebGLCaches: noop,
constructor() {
return this;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't know... let me remove it, i don't remember

}

/**
* 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: {},
/**
* 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;
},
/**
* 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(
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: sourceWidth,
sourceHeight: sourceHeight,
imageData: imageData,
originalEl: sourceElement,
originalImageData: originalImageData,
canvasEl: targetCanvas,
ctx: ctx,
filterBackend: this,
};
})(typeof exports !== 'undefined' ? exports : window);
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;
}
}
30 changes: 25 additions & 5 deletions src/filters/WebGLProbe.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//@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 {
low = 'lowp',
Expand Down Expand Up @@ -45,9 +46,15 @@ class WebGLProbe {
* @param {TWebGLPrecision} 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: TWebGLPrecision
): 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 @@ -62,8 +69,7 @@ class WebGLProbe {
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) =>
Expand All @@ -80,3 +86,17 @@ class WebGLProbe {
}

export const webGLProbe = new WebGLProbe();

export function initFilterBackend():
| WebGLFilterBackend
| Canvas2dFilterBackend {
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;
39 changes: 39 additions & 0 deletions src/filters/typedefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { WebGLFilterBackend } from './webgl_backend.class';
import type { Canvas2dFilterBackend } from './2d_backend.class';

export type TProgramCache = any;

export type TTextureCache = any;

export type TWebGLPipelineState = {
filterBackend: WebGLFilterBackend;
originalWidth: number;
originalHeight: number;
sourceWidth: number;
sourceHeight: number;
destinationWidth: number;
destinationHeight: number;
context: WebGLRenderingContext;
sourceTexture: WebGLTexture | null;
targetTexture: WebGLTexture | null;
originalTexture: WebGLTexture;
passes: number;
webgl: boolean;
aPosition: Float32Array;
programCache: TProgramCache;
pass: number;
targetCanvas: HTMLCanvasElement;
};

export type T2DPipelineState = {
sourceWidth: number;
sourceHeight: number;
filterBackend: Canvas2dFilterBackend;
canvasEl: HTMLCanvasElement;
imageData: ImageData;
originalEl: HTMLCanvasElement | HTMLImageElement;
originalImageData?: ImageData;
ctx: CanvasRenderingContext2D;
};

export type TApplyFilterArgs = {};
Loading