canvaspool and offscreen canvas

This commit is contained in:
2019-01-28 21:26:08 +01:00
parent 23f59be7da
commit 912665885b
9 changed files with 101 additions and 21 deletions

42
src/canvas/CanvasPool.js Normal file
View File

@@ -0,0 +1,42 @@
class CanvasPool {
constructor(ctx) {
this.__ctx = ctx;
this.__pool = [];
this.__pointerCounter = new WeakMap();
}
__createNewCanvas() {
const canvas = (this.__ctx !== 'webgl') ? new OffscreenCanvas(1,1) : document.createElement('canvas');
canvas.acquire = this.acquireCanvas.bind(this, canvas);
canvas.release = this.releaseCanvas.bind(this, canvas);
canvas.getContext(this.__ctx);
this.__pool.push(canvas);
this.__pointerCounter.set(canvas, 0);
}
createCanvas() {
if (this.__pool.length === 0) {
this.__createNewCanvas();
}
return this.__pool.pop();
}
acquireCanvas(canvas) {
const inUse = this.__pointerCounter.get(canvas) + 1;
this.__pointerCounter.set(canvas, inUse);
}
releaseCanvas(canvas) {
const inUse = this.__pointerCounter.get(canvas) - 1;
this.__pointerCounter.set(canvas, inUse);
if (inUse === 0) {
this.__pool.push(canvas);
}
}
}
export const canvasPool2D = new CanvasPool('2d');
export const canvasPoolWebGL = new CanvasPool('webgl');

View File

@@ -1,5 +1,6 @@
import Node from '../Node'; import Node from '../Node';
import {waitForMedia} from '../../utils'; import {waitForMedia} from '../../utils';
import {canvasPool2D} from '../../canvas/CanvasPool';
export default class Canvas2d extends Node { export default class Canvas2d extends Node {
constructor(name, inputDefinition, outputDefiniton, options) { constructor(name, inputDefinition, outputDefiniton, options) {
@@ -22,11 +23,9 @@ export default class Canvas2d extends Node {
}, outputDefiniton), }, outputDefiniton),
options options
); );
this.__canvas = document.createElement('canvas');
} }
destroy() { destroy() {
this.__canvas = null;
super.destroy(); super.destroy();
} }
@@ -35,13 +34,39 @@ export default class Canvas2d extends Node {
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);
} }
const ctx = this.__canvas.getContext('2d');
if (this.__canvas.width > 0 && this.__canvas.height > 0) { const canvas = canvasPool2D.createCanvas();
ctx.clearRect(0,0,this.__canvas.width, this.__canvas.height); canvas.acquire();
const ctx = canvas.getContext('2d');
canvas.width = 1;
canvas.height = 1;
if (values.image && values.image.acquire) {
values.image.acquire();
}
const lastResult = this.__out.image.value;
try {
const result = await this.render(values, canvas, ctx);
this.out.image.value = result;
this.__updateOutputDimensions();
} catch (err) {
console.error('Error in render', err);
canvas.release();
}
if (values.image && values.image.release) {
values.image.release();
}
if (lastResult && lastResult.release) {
lastResult.release();
}
}
__updateOutputDimensions() {
if (!this.out.image.value) {
return;
} }
const result = await this.render(values, this.__canvas, ctx);
if (result instanceof ImageBitmap) {
this.__out.image.value = result;
if (this.out.width.value !== this.out.image.value.width) { if (this.out.width.value !== this.out.image.value.width) {
this.out.width.value = this.out.image.value.width; this.out.width.value = this.out.image.value.width;
} }
@@ -49,7 +74,6 @@ export default class Canvas2d extends Node {
this.out.height.value = this.out.image.value.height; this.out.height.value = this.out.image.value.height;
} }
} }
}
/** /**
@@ -57,7 +81,7 @@ 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>} * @returns {Promise<HTMLCanvasElement|null>}
*/ */
async render(values, canvas, ctx) { async render(values, canvas, ctx) {
return null; 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);
} }
return await createImageBitmap(canvas); return canvas;
} }
} }

View File

@@ -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);
return await createImageBitmap(canvas); return canvas;
} }
} }

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);
return await createImageBitmap(canvas); return canvas;
} }
} }

View File

@@ -68,6 +68,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));
}); });
return await createImageBitmap(canvas); return canvas;
} }
} }

View File

@@ -1,5 +1,6 @@
import Node from '../Node'; import Node from '../Node';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas'; import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {canvasPoolWebGL} from '../../canvas/CanvasPool';
export default class WebGL extends Node { export default class WebGL extends Node {
@@ -138,8 +139,8 @@ export default class WebGL extends Node {
setup() { setup() {
if (!this.canvas) { if (!this.canvas) {
this.canvas = new OffscreenCanvas(1,1);
this.program = null; this.program = null;
this.canvas = createCanvas(0, 0);
} }
const gl = this.canvas.getContext('webgl'); const gl = this.canvas.getContext('webgl');
if (!this.program) { if (!this.program) {
@@ -150,8 +151,12 @@ 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;
if (image.acquire) {
image.acquire();
}
const {width, height} = mediaSize(image); const {width, height} = mediaSize(image);
if (width === 0 || height === 0) { if (width === 0 || height === 0) {
return; return;
@@ -229,7 +234,14 @@ export default class WebGL extends Node {
// Draw the rectangle. // Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6); gl.drawArrays(gl.TRIANGLES, 0, 6);
const result = await createImageBitmap(canvas); const result = canvas.transferToImageBitmap();
// this.canvas = null;
if (image.release) {
image.release();
}
if (this.out.image.value && this.out.image.value.release) {
this.out.image.value.release();
}
this.out.image.value = result; this.out.image.value = result;
if (this.out.width.value !== this.out.image.value.width) { if (this.out.width.value !== this.out.image.value.width) {
this.out.width.value = this.out.image.value.width; this.out.width.value = this.out.image.value.width;

View File

@@ -25,6 +25,7 @@ export const createCanvas = (width, height) => {
* @returns {{width: Number, height: Number}} * @returns {{width: Number, height: Number}}
*/ */
export const mediaSize = (media) => export const mediaSize = (media) =>
(media === null) ? {width: null, height: null} :
(media instanceof HTMLVideoElement) ? {width: media.videoWidth, height: media.videoHeight} : (media instanceof HTMLVideoElement) ? {width: media.videoWidth, height: media.videoHeight} :
(media instanceof HTMLImageElement) ? {width: media.naturalWidth, height: media.naturalHeight} : (media instanceof HTMLImageElement) ? {width: media.naturalWidth, height: media.naturalHeight} :
{width: media.width, height: media.height}; {width: media.width, height: media.height};

View File

@@ -6,6 +6,7 @@ export const defaultConstrainSatisfied = (value) => {
export const imageConstrainSatisfied = (value) => { export const imageConstrainSatisfied = (value) => {
return value instanceof HTMLCanvasElement || return value instanceof HTMLCanvasElement ||
value instanceof OffscreenCanvas ||
value instanceof Image || value instanceof Image ||
value instanceof HTMLVideoElement || value instanceof HTMLVideoElement ||
value instanceof ImageData || value instanceof ImageData ||