♻️ switching to typescript

This commit is contained in:
2019-12-06 20:33:06 +01:00
parent 08e1cb221f
commit 38d8357ecc
40 changed files with 1048 additions and 764 deletions

View File

@@ -1,26 +1,38 @@
import Node from '../Node';
import {waitForMedia} from '../../utils';
import {waitForMedia, merge} from '../../utils';
import {canvasPool2D} from '../../canvas/CanvasPool';
import {
Variables,
ImageVar,
BooleanVar,
NumberVar
} from '../io/AbstractIOSet';
export default class Canvas2d extends Node {
constructor(name, inputDefinition, outputDefiniton, options) {
const inputs = {
image: {
type: 'Image',
} as ImageVar,
};
const outputs = {
image: {
type: 'Image',
} as ImageVar,
width: {
type: 'Number',
} as NumberVar,
height: {
type: 'Number',
} as NumberVar
};
export default class Canvas2d<I extends Variables, O extends Variables> extends Node<I & (typeof inputs), O & (typeof outputs)> {
constructor(name, inputDefinition: I, outputDefiniton: O, options) {
super(name,
Object.assign({
image: {
type: 'Image',
},
}, inputDefinition),
Object.assign({
image: {
type: 'Image',
},
width: {
type: 'Number',
},
height: {
type: 'Number',
}
}, outputDefiniton),
merge(inputs, inputDefinition),
merge(outputs, outputDefiniton),
options
);
}

View File

@@ -1,154 +0,0 @@
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;
}
if (!this.bg && !this.fg) {
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);
}
return canvas;
}
}

View File

@@ -0,0 +1,163 @@
import Canvas2d from './Canvas2d';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {
ImageVar,
StringVar,
NumberVar
} from '../io/AbstractIOSet';
const inputs = {
image: null,
fg: {
type: 'Image'
} as ImageVar,
fgX: {
type: 'Number',
default: 0,
step: 1,
} as NumberVar,
fgY: {
type: 'Number',
default: 0,
step: 1,
} as NumberVar,
bg: {
type: 'Image',
} as ImageVar,
bgX: {
type: 'Number',
default: 0,
step: 1,
} as NumberVar,
bgY: {
type: 'Number',
default: 0,
step: 1,
} as NumberVar,
width: {
type: 'Number',
default: 100,
step: 1,
min: 1
} as NumberVar,
height: {
type: 'Number',
default: 100,
step: 1,
min: 1,
} as NumberVar,
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',
]
} as StringVar
}
const outputs = {
image: {
type: 'Image'
} as ImageVar
}
export default class Compose extends Canvas2d<typeof inputs, typeof outputs> {
constructor(options={}) {
super('Compose', inputs, outputs, 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;
}
if (!this.bg && !this.fg) {
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);
}
return canvas;
}
}

View File

@@ -1,30 +1,35 @@
import Canvas2d from './Canvas2d';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {
NumberVar
} from '../io/AbstractIOSet';
export default class EmptySpace extends Canvas2d {
const inputs = {};
const outputs = {
w: {
type: 'Number',
} as NumberVar,
h: {
type: 'Number',
} as NumberVar,
top: {
type: 'Number',
} as NumberVar,
right: {
type: 'Number',
} as NumberVar,
bottom: {
type: 'Number',
} as NumberVar,
left: {
type: 'Number',
} as NumberVar
};
export default class EmptySpace extends Canvas2d<typeof inputs, typeof outputs> {
constructor() {
super('EmptySpace', {
}, {
w: {
type: 'Number',
},
h: {
type: 'Number',
},
top: {
type: 'Number',
},
right: {
type: 'Number',
},
bottom: {
type: 'Number',
},
left: {
type: 'Number',
}
});
super('EmptySpace', inputs, outputs, {});
this._update();
}
@@ -42,22 +47,23 @@ export default class EmptySpace extends Canvas2d {
canvas.height = height;
ctx.drawImage(image, 0, 0);
const imageData = ctx.getImageData(0, 0, width, height)
for (let offset=0; offset<imageData.data.length; offset+=4) {
const x = (offset/4) % width;
const y = Math.floor((offset/4) / width)
const [r,g,b,a] = imageData.data.slice(offset, offset+4);
if (a < 1) {
let x = 0, y = 0, a, offset;
let length = imageData.data.length/4;
for (offset=0; offset<length; offset+=1) {
x = (offset) % width;
y = Math.floor(offset / width)
a = imageData.data[offset * 4 + 3];
if (a < 254) {
top = Math.min(top, y);
right = Math.max(right, x);
bottom = Math.max(bottom, y);
left = Math.min(left, x);
}
imageData.data[offset] = a;
imageData.data[offset + 1] = a;
imageData.data[offset + 2] = a;
imageData.data[offset + 3] = a;
if (x === left && y == bottom && x < right) {
offset += (right - left) -1;
}
}
ctx.putImageData(imageData, 0, 0);
this.out.top.value = top;
this.out.right.value = right;
this.out.bottom.value = bottom;

View File

@@ -1,33 +0,0 @@
import Canvas2d from './Canvas2d';
export default class Fill extends Canvas2d {
constructor() {
super('Fill', {
image: null,
width: {
type: 'Number',
default: 100,
min: 1,
},
height: {
type: 'Number',
default: 100,
min: 1,
},
color: {
type: 'Color',
default: '#FFFFFF'
}
}, {});
this._update();
}
async render({color, width, height}, canvas, ctx) {
canvas.width = width;
canvas.height = height;
ctx.fillStyle = color;
ctx.fillRect(0, 0, width, height);
return canvas;
}
}

View File

@@ -0,0 +1,40 @@
import Canvas2d from './Canvas2d';
import {
NumberVar,
ColorVar
} from '../io/AbstractIOSet';
const inputs = {
image: null,
width: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
height: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
color: {
type: 'Color',
default: '#FFFFFF'
} as ColorVar
};
const outputs = {};
export default class Fill extends Canvas2d<typeof inputs, typeof outputs> {
constructor() {
super('Fill', inputs, outputs);
this._update();
}
async render({color, width, height}, canvas, ctx) {
canvas.width = width;
canvas.height = height;
ctx.fillStyle = color;
ctx.fillRect(0, 0, width, height);
return canvas;
}
}

View File

@@ -1,19 +1,26 @@
import Canvas2d from './Canvas2d';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {
BooleanVar
} from '../io/AbstractIOSet';
export default class Flip extends Canvas2d {
const inputs = {
horizontal: {
type: 'Boolean',
default: false,
} as BooleanVar,
vertical: {
type: 'Boolean',
default: false,
} as BooleanVar,
}
const outputs = {};
export default class Flip extends Canvas2d<typeof inputs, typeof outputs> {
constructor() {
super('Flip', {
horizontal: {
type: 'Boolean',
default: false,
},
vertical: {
type: 'Boolean',
default: false,
},
}, {});
super('Flip', inputs, outputs, {});
this._update();
}

View File

@@ -1,22 +1,33 @@
import Canvas2d from './Canvas2d';
import {waitForMedia} from '../../utils';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {
ImageVar,
StringVar,
NumberVar
} from '../io/AbstractIOSet';
export default class Resize extends Canvas2d {
const inputs = {
width: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
height: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
};
const outputs = {};
export default class Resize extends Canvas2d<typeof inputs, typeof outputs> {
constructor() {
super('Resize', {
width: {
type: 'Number',
default: 100,
min: 1,
},
height: {
type: 'Number',
default: 100,
min: 1,
},
}, {});
super('Resize', inputs, outputs, {});
}
/**

View File

@@ -1,91 +0,0 @@
import Canvas2d from './Canvas2d';
import {waitForMedia} from '../../utils';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
const isEmpty = (x) => !x || Object.keys(x).length === 0;
export default class Resize extends Canvas2d {
constructor() {
super('Text', {
image: null,
text: {
type: 'String',
default: '',
},
fontSize: {
type: 'Number',
min: 1,
default: 50,
},
fontStyle: {
type: 'String',
default: 'normal',
enum: [
'normal',
'bold',
'italic',
'bold italic',
]
},
font: {
type: 'Font', // {name, url, fontface}
default: 'Arial',
},
width: {
type: 'Number',
default: 100,
min: 1,
},
height: {
type: 'Number',
default: 100,
min: 1,
},
color: {
type: 'Color',
default: '#FFFFFF'
},
textAlign: {
type: 'String',
default: 'center',
enum: [
'left',
'center',
'right',
]
}
}, {});
this._update();
}
/**
*
* @param {{name: string, url: string, fontface: FontFace, type: 'font'}} font
*/
async loadFont(font) {
if (typeof font === 'string') {
return font;
} else if (!font) {
return;
} else {
return font.name;
}
}
async render({font, fontSize, text, color, width, height, textAlign, fontStyle}, canvas, ctx) {
canvas.width = width;
canvas.height = height;
ctx.font = `${fontStyle} ${fontSize}px "${(await this.loadFont(font)) || ''}"`;
// ctx.font = `${fontStyle} ${fontSize}px "Arial"`;
ctx.fillStyle = color;
ctx.textAlign = textAlign;
const x = textAlign === 'center' ? canvas.width /2 :
textAlign === 'left' ? 0 : canvas.width;
text.split('\\n').forEach((line, index) => {
ctx.fillText(line, x, fontSize * (1 + index));
});
return canvas;
}
}

101
src/nodes/Canvas2d/Text.ts Normal file
View File

@@ -0,0 +1,101 @@
import Canvas2d from './Canvas2d';
import {waitForMedia} from '../../utils';
import {createCanvas, mediaSize, paintToCanvas} from '../canvas';
import {
NumberVar,
StringVar,
FontVar,
ColorVar,
} from '../io/AbstractIOSet';
const isEmpty = (x) => !x || Object.keys(x).length === 0;
const inputs = {
image: null,
text: {
type: 'String',
default: '',
} as StringVar,
fontSize: {
type: 'Number',
min: 1,
default: 50,
} as NumberVar,
fontStyle: {
type: 'String',
default: 'normal',
enum: [
'normal',
'bold',
'italic',
'bold italic',
]
} as StringVar,
font: {
type: 'Font', // {name, url, fontface}
default: 'Arial',
} as FontVar,
width: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
height: {
type: 'Number',
default: 100,
min: 1,
} as NumberVar,
color: {
type: 'Color',
default: '#FFFFFF'
} as ColorVar,
textAlign: {
type: 'String',
default: 'center',
enum: [
'left',
'center',
'right',
]
} as StringVar
};
const outputs = {};
export default class Resize extends Canvas2d<typeof inputs, typeof outputs> {
constructor() {
super('Text', inputs, outputs, {});
this._update();
}
/**
*
* @param {{name: string, url: string, fontface: FontFace, type: 'font'}} font
*/
async loadFont(font) {
if (typeof font === 'string') {
return font;
} else if (!font) {
return;
} else {
return font.name;
}
}
async render({font, fontSize, text, color, width, height, textAlign, fontStyle}, canvas, ctx) {
canvas.width = width;
canvas.height = height;
ctx.font = `${fontStyle} ${fontSize}px "${(await this.loadFont(font)) || ''}"`;
// ctx.font = `${fontStyle} ${fontSize}px "Arial"`;
ctx.fillStyle = color;
ctx.textAlign = textAlign;
const x = textAlign === 'center' ? canvas.width /2 :
textAlign === 'left' ? 0 : canvas.width;
text.split('\\n').forEach((line, index) => {
ctx.fillText(line, x, fontSize * (1 + index));
});
return canvas;
}
}