⚡ postpone update to next microtask
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "graphfx",
|
"name": "graphfx",
|
||||||
"version": "0.0.1",
|
"version": "0.0.3",
|
||||||
"description": "Graph image processing pipeline",
|
"description": "Graph image processing pipeline",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export default class Canvas2d extends Node {
|
|||||||
Object.assign({
|
Object.assign({
|
||||||
image: {
|
image: {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: 'Number',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: 'Number',
|
||||||
}
|
}
|
||||||
}, outputDefiniton),
|
}, outputDefiniton),
|
||||||
options
|
options
|
||||||
@@ -19,7 +25,7 @@ export default class Canvas2d extends Node {
|
|||||||
this.__canvas = document.createElement('canvas');
|
this.__canvas = document.createElement('canvas');
|
||||||
}
|
}
|
||||||
|
|
||||||
async __update() {
|
async _update() {
|
||||||
const values = {};
|
const values = {};
|
||||||
for (let name of Object.keys(this.in.variables)) {
|
for (let name of Object.keys(this.in.variables)) {
|
||||||
values[name] = await waitForMedia(this.in[name].value);
|
values[name] = await waitForMedia(this.in[name].value);
|
||||||
@@ -28,7 +34,12 @@ export default class Canvas2d extends Node {
|
|||||||
if (this.__canvas.width > 0 && this.__canvas.height > 0) {
|
if (this.__canvas.width > 0 && this.__canvas.height > 0) {
|
||||||
ctx.clearRect(0,0,this.__canvas.width, this.__canvas.height);
|
ctx.clearRect(0,0,this.__canvas.width, this.__canvas.height);
|
||||||
}
|
}
|
||||||
await this.render(values, this.__canvas, ctx);
|
const result = await this.render(values, this.__canvas, ctx);
|
||||||
|
if (result instanceof ImageBitmap) {
|
||||||
|
this.__out.image.value = result;
|
||||||
|
this.out.width.value = this.out.image.value.width;
|
||||||
|
this.out.height.value = this.out.image.value.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -37,8 +48,9 @@ export default class Canvas2d extends Node {
|
|||||||
* @param {any} values
|
* @param {any} values
|
||||||
* @param {HTMLCanvasElement} canvas
|
* @param {HTMLCanvasElement} canvas
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
|
* @returns {Promise<ImageBitmap|null>}
|
||||||
*/
|
*/
|
||||||
async render(values, canvas, ctx) {
|
async render(values, canvas, ctx) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,6 +145,6 @@ export default class Compose extends Canvas2d {
|
|||||||
paintToCanvas(canvas, this.fg.image, this.fg);
|
paintToCanvas(canvas, this.fg.image, this.fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.__out.image.value = await createImageBitmap(canvas);
|
return await createImageBitmap(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ export default class Fill extends Canvas2d {
|
|||||||
default: '#FFFFFF'
|
default: '#FFFFFF'
|
||||||
}
|
}
|
||||||
}, {});
|
}, {});
|
||||||
this.__update();
|
this._update();
|
||||||
}
|
}
|
||||||
|
|
||||||
async render({color, width, height}, canvas, ctx) {
|
async render({color, width, height}, canvas, ctx) {
|
||||||
@@ -28,6 +28,6 @@ export default class Fill extends Canvas2d {
|
|||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
this.__out.image.value = await createImageBitmap(canvas);
|
return await createImageBitmap(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,6 @@ export default class ImageIdentity extends Node {
|
|||||||
const dataUrl = canvas.toDataURL()
|
const dataUrl = canvas.toDataURL()
|
||||||
const i = new Image();
|
const i = new Image();
|
||||||
i.src = dataUrl;
|
i.src = dataUrl;
|
||||||
await waitForMedia(i);
|
return await waitForMedia(i);
|
||||||
this.__out.image.value = i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,6 @@ export default class Resize extends Canvas2d {
|
|||||||
newImage.left = (destWidth - newImage.width) / 2;
|
newImage.left = (destWidth - newImage.width) / 2;
|
||||||
|
|
||||||
paintToCanvas(canvas, media, newImage);
|
paintToCanvas(canvas, media, newImage);
|
||||||
this.__out.image.value = await createImageBitmap(canvas);
|
return await createImageBitmap(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ export default class Resize extends Canvas2d {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}, {});
|
}, {});
|
||||||
this.__update();
|
this._update();
|
||||||
}
|
}
|
||||||
|
|
||||||
async render({font, fontSize, text, color, width, height, textAlign, fontStyle}, canvas, ctx) {
|
async render({font, fontSize, text, color, width, height, textAlign, fontStyle}, canvas, ctx) {
|
||||||
@@ -69,6 +69,6 @@ export default class Resize extends Canvas2d {
|
|||||||
text.split('\\n').forEach((line, index) => {
|
text.split('\\n').forEach((line, index) => {
|
||||||
ctx.fillText(line, x, fontSize * (1 + index));
|
ctx.fillText(line, x, fontSize * (1 + index));
|
||||||
});
|
});
|
||||||
this.__out.image.value = await createImageBitmap(canvas);
|
return await createImageBitmap(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ export default class ImageIdentity extends Node {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
__update() {
|
_update() {
|
||||||
this.__out.image.value = this.__in.image.value;
|
this.__out.image.value = this.__in.image.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
56
src/nodes/Math/NumberBinaryOperation.js
Normal file
56
src/nodes/Math/NumberBinaryOperation.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import Node from '../Node';
|
||||||
|
|
||||||
|
export default class NumberBinaryOperation extends Node {
|
||||||
|
constructor() {
|
||||||
|
super('NumberBinaryOperation', {
|
||||||
|
x: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
operation: {
|
||||||
|
type: 'String',
|
||||||
|
default: '+',
|
||||||
|
enum: [
|
||||||
|
'+',
|
||||||
|
'-',
|
||||||
|
'⨉',
|
||||||
|
'÷',
|
||||||
|
'mod',
|
||||||
|
'max',
|
||||||
|
'min',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
result: {
|
||||||
|
type: 'Number'
|
||||||
|
},
|
||||||
|
}, {});
|
||||||
|
this.__canvas = document.createElement('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
async _update() {
|
||||||
|
const x = this.in.x.value;
|
||||||
|
const y = this.in.y.value;
|
||||||
|
const op = this.in.operation.value;
|
||||||
|
const value =
|
||||||
|
op === '+' ? x + y :
|
||||||
|
op === '-' ? x - y :
|
||||||
|
op === '⨉' ? x * y :
|
||||||
|
op === '÷' ?
|
||||||
|
y === 0 ? NaN :
|
||||||
|
x / y :
|
||||||
|
op === 'mod' ? x % y :
|
||||||
|
op === 'max' ? Math.max(x, y) :
|
||||||
|
op === 'min' ? Math.min(x, y) :
|
||||||
|
NaN;
|
||||||
|
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
this.out.result.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
src/nodes/Math/Numbers.js
Normal file
41
src/nodes/Math/Numbers.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import Node from '../Node';
|
||||||
|
|
||||||
|
export default class Numbers extends Node {
|
||||||
|
constructor() {
|
||||||
|
super('Numbers', {
|
||||||
|
x: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
z: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
x: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
z: {
|
||||||
|
type: 'Number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
}, {});
|
||||||
|
this.__canvas = document.createElement('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
async _update() {
|
||||||
|
this.out.x.value = this.in.x.value;
|
||||||
|
this.out.y.value = this.in.y.value;
|
||||||
|
this.out.z.value = this.in.z.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,10 +9,22 @@ export default class Node {
|
|||||||
this.__in = new Inputs(inputDefinition, this);
|
this.__in = new Inputs(inputDefinition, this);
|
||||||
this.__out = new Outputs(outputDefiniton, this);
|
this.__out = new Outputs(outputDefiniton, this);
|
||||||
this.__in.update = (name) => this.__update([name]);
|
this.__in.update = (name) => this.__update([name]);
|
||||||
|
this.__scheduledUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
__update(changes) {
|
__update(changes) {
|
||||||
throw new Error('__update method not implemented');
|
if (!this.__scheduledUpdate) {
|
||||||
|
this.__scheduledUpdate = true;
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
this.__scheduledUpdate = false;
|
||||||
|
this._update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_update(){
|
||||||
|
throw new Error('_update method not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default class ToBlob extends Node {
|
|||||||
return this.options.quality;
|
return this.options.quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
__update() {
|
_update() {
|
||||||
const {width, height} = this.__in.image.value;
|
const {width, height} = this.__in.image.value;
|
||||||
const canvas = createCanvas(width, height);
|
const canvas = createCanvas(width, height);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export default class WebGL extends Node {
|
|||||||
image: {
|
image: {
|
||||||
type: 'Image',
|
type: 'Image',
|
||||||
},
|
},
|
||||||
|
width: {
|
||||||
|
type: 'Number',
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: 'Number',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +143,7 @@ export default class WebGL extends Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async __update() {
|
async _update() {
|
||||||
const image = this.in.image.value;
|
const image = this.in.image.value;
|
||||||
if (!image) return;
|
if (!image) return;
|
||||||
const {width, height} = mediaSize(image);
|
const {width, height} = mediaSize(image);
|
||||||
@@ -214,7 +220,10 @@ export default class WebGL extends Node {
|
|||||||
// Draw the rectangle.
|
// Draw the rectangle.
|
||||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||||
|
|
||||||
this.out.image.value = await createImageBitmap(canvas);
|
const result = await createImageBitmap(canvas);
|
||||||
|
this.out.image.value = result;
|
||||||
|
this.out.width.value = this.out.image.value.width;
|
||||||
|
this.out.height.value = this.out.image.value.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRectangle(gl, x, y, width, height) {
|
setRectangle(gl, x, y, width, height) {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
export {default as Webcam} from './Webcam';
|
export {default as Webcam} from './Webcam';
|
||||||
|
// 2D
|
||||||
export {default as Resize} from './Canvas2d/Resize';
|
export {default as Resize} from './Canvas2d/Resize';
|
||||||
export {default as Compose} from './Canvas2d/Compose';
|
export {default as Compose} from './Canvas2d/Compose';
|
||||||
export {default as Fill} from './Canvas2d/Fill';
|
export {default as Fill} from './Canvas2d/Fill';
|
||||||
// export {default as ImageIdentity} from './Canvas2d/ImageIdentity';
|
// export {default as ImageIdentity} from './Canvas2d/ImageIdentity';
|
||||||
export {default as Text} from './Canvas2d/Text';
|
export {default as Text} from './Canvas2d/Text';
|
||||||
|
// WebGL
|
||||||
export {default as BrightnessContrast} from './WebGL/BrightnessContrast';
|
export {default as BrightnessContrast} from './WebGL/BrightnessContrast';
|
||||||
export {default as HSV} from './WebGL/HSV';
|
export {default as HSV} from './WebGL/HSV';
|
||||||
export {default as Sepia} from './WebGL/Sepia';
|
export {default as Sepia} from './WebGL/Sepia';
|
||||||
export {default as GreenScreen} from './WebGL/Greenscreen';
|
export {default as GreenScreen} from './WebGL/Greenscreen';
|
||||||
export {default as Channels} from './WebGL/Channels';
|
export {default as Channels} from './WebGL/Channels'
|
||||||
|
// Math
|
||||||
|
export {default as Numbers} from './Math/Numbers';
|
||||||
|
export {default as NumberBinaryOperation} from './Math/NumberBinaryOperation';
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ export const waitForMedia = async (media) => {
|
|||||||
}
|
}
|
||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setImmediate = (fn) => Promise.resolve().then(fn);
|
||||||
Reference in New Issue
Block a user