camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
298 lines (297 loc) • 10.6 kB
JavaScript
import { EventEmitter } from 'events';
export class Frame extends EventEmitter {
enabled;
posX;
posY;
width;
height;
text = '';
fontColor;
fontName;
font;
align = 'A_LEFT';
textType = 'TFM_OVERFLOW';
bgColor;
bgImageName;
bgImage;
bgType;
borderRadius;
borderWidth;
borderColor;
customDraw;
layer;
children = new Array();
constructor(opt) {
super();
this.enabled = opt.enabled ?? true;
this.posX = opt.x;
this.posY = opt.y;
this.width = opt.width;
this.height = opt.height;
this.setText(opt.text ?? '', 'A_LEFT');
this.fontColor = opt.fontColor ?? [1.0, 1.0, 1.0];
this.fontName = opt.font;
this.bgColor = opt.bgColor;
this.bgImageName = opt.bgImage;
this.bgType = opt.bgType;
this.borderRadius = opt.borderRadius ?? 0;
this.borderWidth = opt.borderWidth ?? 0;
this.borderColor = opt.borderColor ?? [1, 1, 1, 1];
this.customDraw = opt.customDraw;
this.layer = opt.layer ?? 0;
}
enable() {
this.enabled = true;
}
disable() {
this.enabled = false;
}
setFramePosition(x, y) {
this.posX = x;
this.posY = y;
}
setFrameSize(width, height) {
this.width = width;
this.height = height;
}
setText(text, align, textType = 'TFM_OVERFLOW', fontColor) {
this.text = text;
this.align = align;
this.textType = textType;
if (fontColor) {
this.fontColor = fontColor;
}
}
setFontColor(fontColor) {
this.fontColor = fontColor;
}
setFont(fontName) {
this.fontName = fontName;
this.font = undefined;
}
setFontData(fontData) {
this.fontName = undefined;
this.font = fontData;
}
setBgColor(color) {
this.bgColor = color;
}
setBgImage(imageName, type = 'fit') {
this.bgImageName = imageName;
this.bgImage = undefined;
this.bgType = type;
}
setBgImageData(imageData, type = 'fit') {
this.bgImageName = undefined;
this.bgImage = imageData;
this.bgType = type;
}
setBgType(type) {
this.bgType = type;
}
setBorderRadius(radius) {
this.borderRadius = radius;
}
setBorderWidth(width) {
this.borderWidth = width;
}
setBorderColor(color) {
this.borderColor = color;
}
setCustomDraw(customDraw) {
this.customDraw = customDraw;
}
resetFont() {
this.font = undefined;
}
resetBgColor() {
this.bgColor = undefined;
}
resetBgImage() {
this.bgImage = undefined;
this.bgType = undefined;
}
resetCustomDraw() {
this.customDraw = undefined;
}
clear() {
this.text = '';
this.align = 'A_LEFT';
this.textType = 'TFM_OVERFLOW';
this.fontColor = [1.0, 1.0, 1.0];
this.font = undefined;
this.bgColor = undefined;
this.bgImage = undefined;
this.bgType = undefined;
this.customDraw = undefined;
}
insert(...frames) {
this.children.push(...frames);
for (const frame of frames) {
frame.on('layoutChanged', () => this.layoutChanged());
}
this.layoutChanged();
}
getLayers() {
const uniqueLayers = new Set();
uniqueLayers.add(this.layer);
for (const child of this.children) {
for (const layer of child.getLayers()) {
uniqueLayers.add(layer);
}
}
return uniqueLayers;
}
layoutChanged() {
this.emit('layoutChanged');
}
async displayImage(cod, resourceManager, cairo, ppX, ppY, scale, layer) {
if (this.enabled) {
const posX = ppX + this.posX;
const posY = ppY + this.posY;
await this.prepareResources(resourceManager);
await cod.cairo('cairo_save', cairo);
await this.clipDrawing(cod, cairo, scale, posX, posY);
if (this.layer === layer) {
await this.displayOwnImage(cod, cairo, posX, posY, scale);
}
for (const child of this.children) {
await child.displayImage(cod, resourceManager, cairo, posX, posY, scale, layer);
}
await cod.cairo('cairo_restore', cairo);
}
}
async prepareResources(resourceManager) {
if (this.bgImageName !== undefined) {
this.bgImage = await resourceManager.image(this.bgImageName);
}
if (this.fontName !== undefined) {
this.font = await resourceManager.font(this.fontName);
}
}
async displayOwnImage(cod, cairo, ppX, ppY, scale) {
if (!this.enabled) {
return;
}
const promises = new Array();
if (this.font !== undefined) {
promises.push(cod.cairo('cairo_set_font_face', cairo, this.font.var));
}
else {
promises.push(cod.cairo('cairo_set_font_face', cairo, 'NULL'));
}
if (this.bgColor !== undefined) {
promises.push(this.drawFrame(cod, cairo, scale, ppX, ppY));
}
if (this.bgImage !== undefined) {
promises.push(this.drawImage(cod, cairo, scale, ppX, ppY));
}
if (this.borderWidth > 0) {
promises.push(this.drawBorder(cod, cairo, scale, ppX, ppY));
}
if (this.text) {
promises.push(this.drawText(cod, cairo, scale, ppX, ppY));
}
if (this.customDraw) {
promises.push(cod.cairo('cairo_identity_matrix', cairo));
promises.push(cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY));
promises.push(cod.cairo('cairo_scale', cairo, scale, scale));
promises.push(this.customDraw(cod, cairo, { width: this.width, height: this.height }));
}
await Promise.all(promises);
}
async clipDrawing(cod, cairo, scale, ppX, ppY) {
if (this.borderRadius === 0) {
return;
}
await Promise.all([
this.drawRectPath(cod, cairo, scale, ppX, ppY, this.borderRadius),
cod.cairo('cairo_clip', cairo),
]);
}
async drawFrame(cod, cairo, scale, ppX, ppY) {
if (this.bgColor) {
const promises = [
cod.cairo('cairo_identity_matrix', cairo),
cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY),
cod.cairo('cairo_scale', cairo, scale, scale),
cod.cairo('cairo_set_source_rgba', cairo, this.bgColor[0], this.bgColor[1], this.bgColor[2], this.bgColor[3]),
cod.cairo('cairo_rectangle', cairo, 0, 0, this.width, this.height),
cod.cairo('cairo_fill', cairo),
cod.cairo('cairo_stroke', cairo),
];
await Promise.all(promises);
}
else {
throw new Error('Color of the frame is undefined.');
}
}
async drawImage(cod, cairo, scale, ppX, ppY) {
if (this.bgImage === undefined) {
return;
}
const bgImage = this.bgImage.var;
const bgWidth = this.bgImage.width;
const bgHeight = this.bgImage.height;
const promises = new Array();
promises.push(cod.cairo('cairo_identity_matrix', cairo));
promises.push(cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY));
if (this.bgType === 'fill') {
const sx = (scale * this.width) / bgWidth;
const sy = (scale * this.height) / bgHeight;
promises.push(cod.cairo('cairo_scale', cairo, sx, sy));
}
else if (this.bgType === 'fit') {
const sx = this.width / bgWidth;
const sy = this.height / bgHeight;
const scaleRatio = scale * Math.min(sx, sy);
promises.push(cod.cairo('cairo_scale', cairo, scaleRatio, scaleRatio));
}
else {
promises.push(cod.cairo('cairo_scale', cairo, scale, scale));
}
promises.push(cod.cairo('cairo_set_source_surface', cairo, bgImage, 0, 0));
promises.push(cod.cairo('cairo_paint', cairo));
await Promise.all(promises);
}
async drawText(cod, cairo, scale, ppX, ppY) {
const promises = [
cod.cairo('cairo_identity_matrix', cairo),
cod.cairo('cairo_set_source_rgb', cairo, this.fontColor[0], this.fontColor[1], this.fontColor[2]),
cod.writeText(cairo, '' + this.text, Math.floor(scale * ppX), Math.floor(scale * ppY), Math.floor(scale * this.width), Math.floor(scale * this.height), this.align, this.textType),
];
await Promise.all(promises);
}
async drawBorder(cod, cairo, scale, ppX, ppY) {
await Promise.all([
this.drawRectPath(cod, cairo, scale, ppX, ppY, this.borderRadius),
cod.cairo('cairo_set_source_rgba', cairo, this.borderColor[0], this.borderColor[1], this.borderColor[2], this.borderColor[3]),
cod.cairo('cairo_set_line_width', cairo, this.borderWidth),
cod.cairo('cairo_stroke', cairo),
]);
}
async drawRectPath(cod, cairo, scale, ppX, ppY, radius) {
if (radius === 0) {
return await Promise.all([
cod.cairo('cairo_identity_matrix', cairo),
cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY),
cod.cairo('cairo_scale', cairo, scale, scale),
cod.cairo('cairo_rectangle', cairo, 0, 0, this.width, this.height),
]);
}
else {
const degrees = Math.PI / 180;
return await Promise.all([
cod.cairo('cairo_identity_matrix', cairo),
cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY),
cod.cairo('cairo_scale', cairo, scale, scale),
cod.cairo('cairo_new_sub_path', cairo),
cod.cairo('cairo_arc', cairo, this.width - radius, radius, radius, -90 * degrees, 0 * degrees),
cod.cairo('cairo_arc', cairo, this.width - radius, this.height - radius, radius, 0 * degrees, 90 * degrees),
cod.cairo('cairo_arc', cairo, radius, this.height - radius, radius, 90 * degrees, 180 * degrees),
cod.cairo('cairo_arc', cairo, radius, radius, radius, 180 * degrees, 270 * degrees),
cod.cairo('cairo_close_path', cairo),
]);
}
}
}