UNPKG

camstreamerlib

Version:

Helper library for CamStreamer ACAP applications.

298 lines (297 loc) 10.6 kB
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), ]); } } }