⚡ reusing canvas, passing imageBitmap instead
This commit is contained in:
44
src/nodes/Canvas2d/Canvas2d.js
Normal file
44
src/nodes/Canvas2d/Canvas2d.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import Node from '../Node';
|
||||
import {waitForMedia} from '../../utils';
|
||||
|
||||
export default class Canvas2d extends Node {
|
||||
constructor(name, inputDefinition, outputDefiniton, options) {
|
||||
super(name,
|
||||
Object.assign({
|
||||
image: {
|
||||
type: 'Image',
|
||||
},
|
||||
}, inputDefinition),
|
||||
Object.assign({
|
||||
image: {
|
||||
type: 'Image',
|
||||
}
|
||||
}, outputDefiniton),
|
||||
options
|
||||
);
|
||||
this.__canvas = document.createElement('canvas');
|
||||
}
|
||||
|
||||
async __update() {
|
||||
const values = {};
|
||||
for (let name of Object.keys(this.in.variables)) {
|
||||
values[name] = await waitForMedia(this.in[name].value);
|
||||
}
|
||||
const ctx = this.__canvas.getContext('2d');
|
||||
if (this.__canvas.width > 0 && this.__canvas.height > 0) {
|
||||
ctx.clearRect(0,0,this.__canvas.width, this.__canvas.height);
|
||||
}
|
||||
await this.render(values, this.__canvas, ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} values
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
async render(values, canvas, ctx) {
|
||||
|
||||
}
|
||||
}
|
||||
150
src/nodes/Canvas2d/Compose.js
Normal file
150
src/nodes/Canvas2d/Compose.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import Canvas2d from './Canvas2d';
|
||||
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
|
||||
|
||||
export default class Compose extends Canvas2d {
|
||||
|
||||
constructor(options) {
|
||||
super('Compose', {
|
||||
image: null,
|
||||
fg: {
|
||||
type: 'Image'
|
||||
},
|
||||
fgX: {
|
||||
type: 'Number',
|
||||
default: 0,
|
||||
step: 1,
|
||||
},
|
||||
fgY: {
|
||||
type: 'Number',
|
||||
default: 0,
|
||||
step: 1,
|
||||
},
|
||||
bg: {
|
||||
type: 'Image',
|
||||
},
|
||||
bgX: {
|
||||
type: 'Number',
|
||||
default: 0,
|
||||
step: 1,
|
||||
},
|
||||
bgY: {
|
||||
type: 'Number',
|
||||
default: 0,
|
||||
step: 1,
|
||||
},
|
||||
width: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
step: 1,
|
||||
min: 1
|
||||
},
|
||||
height: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
step: 1,
|
||||
min: 1,
|
||||
},
|
||||
mode: {
|
||||
type: 'String',
|
||||
default: 'source-over',
|
||||
enum: [
|
||||
'source-over',
|
||||
'source-in',
|
||||
'source-out',
|
||||
'source-atop',
|
||||
'destination-over',
|
||||
'destination-in',
|
||||
'destination-out',
|
||||
'destination-atop',
|
||||
'lighter',
|
||||
'copy',
|
||||
'xor',
|
||||
'multiply',
|
||||
'screen',
|
||||
'overlay',
|
||||
'darken',
|
||||
'lighten',
|
||||
'color-dodge',
|
||||
'color-burn',
|
||||
'hard-light',
|
||||
'soft-light',
|
||||
'difference',
|
||||
'exclusion',
|
||||
'hue',
|
||||
'saturation',
|
||||
'color',
|
||||
'luminosity',
|
||||
]
|
||||
}
|
||||
}, {
|
||||
image: {
|
||||
type: 'Image'
|
||||
}
|
||||
}, options);
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this.__in.width.value;
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.__in.height.value;
|
||||
}
|
||||
|
||||
get fg() {
|
||||
if (!this.__in.fg.value) {
|
||||
return null;
|
||||
} else {
|
||||
const {width, height} = this.__in.fg.value;
|
||||
return {
|
||||
image: this.__in.fg.value,
|
||||
top: this.__in.fgY.value || 0,
|
||||
left: this.__in.fgX.value || 0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
get bg() {
|
||||
if (!this.__in.bg.value) {
|
||||
return null;
|
||||
} else {
|
||||
const {width, height} = this.__in.bg.value;
|
||||
return {
|
||||
image: this.__in.bg.value,
|
||||
top: this.__in.bgY.value || 0,
|
||||
left: this.__in.bgX.value || 0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} values
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
async render({width, height, mode}, canvas, ctx) {
|
||||
if (!width || !height) {
|
||||
return;
|
||||
}
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
|
||||
if (this.bg) {
|
||||
paintToCanvas(canvas, this.bg.image, this.bg);
|
||||
}
|
||||
|
||||
ctx.globalCompositeOperation = mode;
|
||||
|
||||
if (this.fg) {
|
||||
paintToCanvas(canvas, this.fg.image, this.fg);
|
||||
}
|
||||
|
||||
this.__out.image.value = await createImageBitmap(canvas);
|
||||
}
|
||||
}
|
||||
26
src/nodes/Canvas2d/ImageIdentity.js
Normal file
26
src/nodes/Canvas2d/ImageIdentity.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import Node from './Canvas2d';
|
||||
import {waitForMedia} from '../../utils';
|
||||
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
|
||||
|
||||
|
||||
export default class ImageIdentity extends Node {
|
||||
constructor() {
|
||||
super('ImageIdentity', {
|
||||
}, {
|
||||
});
|
||||
}
|
||||
|
||||
async render({image}) {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
const {width, height} = mediaSize(image);
|
||||
const canvas = createCanvas(width, height);
|
||||
paintToCanvas(canvas, image, {width, height, top: 0, left: 0});
|
||||
const dataUrl = canvas.toDataURL()
|
||||
const i = new Image();
|
||||
i.src = dataUrl;
|
||||
await waitForMedia(i);
|
||||
this.__out.image.value = i;
|
||||
}
|
||||
}
|
||||
59
src/nodes/Canvas2d/Resize.js
Normal file
59
src/nodes/Canvas2d/Resize.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import Canvas2d from './Canvas2d';
|
||||
import {waitForMedia} from '../../utils';
|
||||
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
|
||||
|
||||
export default class Resize extends Canvas2d {
|
||||
|
||||
constructor() {
|
||||
super('Resize', {
|
||||
width: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
min: 1,
|
||||
},
|
||||
height: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
min: 1,
|
||||
},
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} values
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
*/
|
||||
async render({image: media, width, height}, canvas, ctx) {
|
||||
await waitForMedia(media)
|
||||
if (!media) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!width || !height) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const {width: srcWidth, height: srcHeight} = mediaSize(media);
|
||||
const {width: destWidth, height: destHeight} = mediaSize(canvas);
|
||||
const srcAspectRatio = (srcWidth / srcHeight);
|
||||
const destAspectRatio = (destWidth / destHeight);
|
||||
const newImage = {};
|
||||
if (srcAspectRatio > destAspectRatio) {
|
||||
newImage.width = destHeight * srcAspectRatio;
|
||||
newImage.height = destHeight;
|
||||
} else {
|
||||
newImage.width = destWidth;
|
||||
newImage.height = destWidth / srcAspectRatio;
|
||||
}
|
||||
|
||||
newImage.top = (destHeight - newImage.height) / 2;
|
||||
newImage.left = (destWidth - newImage.width) / 2;
|
||||
|
||||
paintToCanvas(canvas, media, newImage);
|
||||
this.__out.image.value = await createImageBitmap(canvas);
|
||||
}
|
||||
}
|
||||
49
src/nodes/Canvas2d/Text.js
Normal file
49
src/nodes/Canvas2d/Text.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import Canvas2d from './Canvas2d';
|
||||
import {waitForMedia} from '../../utils';
|
||||
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
|
||||
|
||||
export default class Resize extends Canvas2d {
|
||||
|
||||
constructor() {
|
||||
super('Text', {
|
||||
image: null,
|
||||
text: {
|
||||
type: 'String',
|
||||
default: '',
|
||||
},
|
||||
fontSize: {
|
||||
type: 'Number',
|
||||
min: 1,
|
||||
default: 50,
|
||||
},
|
||||
font: {
|
||||
type: 'String',
|
||||
default: 'Arial',
|
||||
},
|
||||
width: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
min: 1,
|
||||
},
|
||||
height: {
|
||||
type: 'Number',
|
||||
default: 100,
|
||||
min: 1,
|
||||
},
|
||||
color: {
|
||||
type: 'Color',
|
||||
default: '#FFFFFF'
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
|
||||
async render({font, fontSize, text, color, width, height}, canvas, ctx) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
ctx.font = `${fontSize}px ${font}`;
|
||||
ctx.fillStyle = color;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(text, canvas.width/2, canvas.height/2);
|
||||
this.__out.image.value = await createImageBitmap(canvas);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user