postpone update to next microtask

This commit is contained in:
2019-01-25 19:27:11 +01:00
parent c07a74d3a2
commit 43736b0415
15 changed files with 156 additions and 20 deletions

View File

@@ -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": {

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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;
} }
} }

View 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
View 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;
}
}

View File

@@ -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');
} }
/** /**

View File

@@ -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');

View File

@@ -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) {

View File

@@ -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';

View File

@@ -8,3 +8,5 @@ export const waitForMedia = async (media) => {
} }
return media; return media;
} }
export const setImmediate = (fn) => Promise.resolve().then(fn);