camstreamerlib
Version:
Helper library for CamStreamer ACAP applications.
167 lines (166 loc) • 6.18 kB
JavaScript
import { CamOverlayDrawingAPI } from '../CamOverlayDrawingAPI';
import ResourceManager from './ResourceManager';
import { Frame } from './Frame';
export const COORD = {
top_left: [-1, -1],
center_left: [-1, 0],
bottom_left: [-1, 1],
top_center: [0, -1],
center: [0, 0],
bottom_center: [0, 1],
top_right: [1, -1],
center_right: [1, 0],
bottom_right: [1, 1],
};
export class Painter extends Frame {
screenWidth;
screenHeight;
coAlignment;
cod;
rm;
refreshLayers = true;
layers = [];
constructor(opt, coopt) {
super(opt);
this.coAlignment = COORD[opt.coAlignment];
this.screenWidth = opt.screenWidth;
this.screenHeight = opt.screenHeight;
this.cod = new CamOverlayDrawingAPI(coopt);
this.rm = new ResourceManager(this.cod);
}
get camOverlayDrawingAPI() {
return this.cod;
}
get resourceManager() {
return this.rm;
}
connect() {
this.cod.on('open', () => {
this.rm.clear();
this.layers = [];
this.refreshLayers = true;
this.emit('open');
});
this.cod.on('close', () => {
this.emit('close');
});
this.cod.on('error', (err) => {
console.error('Painter:', err);
});
this.cod.connect();
}
disconnect() {
this.cod.disconnect();
}
isConnected() {
return this.cod.isConnected();
}
registerImage(moniker, fileName) {
this.rm.registerImage(moniker, fileName);
}
registerFont(moniker, fileName) {
this.rm.registerFont(moniker, fileName);
}
setScreenSize(sw, sh) {
this.screenWidth = sw;
this.screenHeight = sh;
}
setCoAlignment(coAlignment) {
this.coAlignment = COORD[coAlignment];
}
layoutChanged() {
this.refreshLayers = true;
}
async display(scale = 1.0) {
if (this.enabled) {
if (this.refreshLayers) {
this.refreshLayers = false;
await this.prepareLayers();
}
let cairo;
let surface;
let lastCachedLayer;
for (let i = 0; i < this.layers.length; i++) {
const layer = this.layers[i];
if (layer === undefined) {
continue;
}
if (layer.cairoCache !== undefined &&
layer.surfaceCache !== undefined &&
surface === undefined &&
cairo === undefined) {
lastCachedLayer = layer;
continue;
}
if (surface === undefined || cairo === undefined) {
[surface, cairo] = await this.prepareSurface(scale, lastCachedLayer?.surfaceCache, lastCachedLayer?.cairoCache);
}
await this.displayImage(this.cod, this.rm, cairo, -this.posX, -this.posY, scale, layer.layer);
if (i < this.layers.length - 1) {
const [surfaceToCache, cairoToCache] = await this.prepareSurface(scale, surface, cairo);
layer.surfaceCache = surfaceToCache;
layer.cairoCache = cairoToCache;
}
}
if (surface !== undefined && cairo !== undefined) {
await this.cod.showCairoImage(surface, this.positionConvertor(this.coAlignment[0], this.screenWidth, this.posX, scale * this.width), this.positionConvertor(this.coAlignment[1], this.screenHeight, this.posY, scale * this.height));
await this.cleanupSurface(surface, cairo);
}
}
}
async hide() {
await this.cod.removeImage();
}
async invalidateLayer(layer) {
const layerIdx = this.layers.findIndex((l) => l.layer === layer);
if (layerIdx === -1) {
return;
}
for (let i = layerIdx; i < this.layers.length; i++) {
const currentLayer = this.layers[i];
if (currentLayer?.surfaceCache !== undefined && currentLayer.cairoCache !== undefined) {
await this.cleanupSurface(currentLayer.surfaceCache, currentLayer.cairoCache);
currentLayer.surfaceCache = undefined;
currentLayer.cairoCache = undefined;
}
}
}
async prepareLayers() {
for (const layer of this.layers) {
if (layer.surfaceCache !== undefined && layer.cairoCache !== undefined) {
await this.cleanupSurface(layer.surfaceCache, layer.cairoCache);
}
}
const uniqueLayers = this.getLayers();
const sortedLayers = Array.from(uniqueLayers).sort((a, b) => a - b);
this.layers = sortedLayers.map((layer) => {
return { layer };
});
}
async prepareSurface(scale, cachedSurface, cachedCairo) {
const surface = (await this.cod.cairo('cairo_image_surface_create', 'CAIRO_FORMAT_ARGB32', Math.floor(this.width * scale), Math.floor(this.height * scale)));
const cairo = (await this.cod.cairo('cairo_create', surface.var));
if (cachedSurface !== undefined && cachedCairo !== undefined) {
await this.cod.cairo('cairo_set_source_surface', cairo.var, cachedSurface, 0, 0);
await this.cod.cairo('cairo_paint', cairo.var);
}
return [surface.var, cairo.var];
}
async cleanupSurface(surface, cairo) {
await this.cod.cairo('cairo_surface_destroy', surface);
await this.cod.cairo('cairo_destroy', cairo);
}
positionConvertor(alignment, screenSize, position, graphicsSize) {
switch (alignment) {
case -1:
return alignment + (2.0 * position) / screenSize;
case 0:
return alignment - (2.0 * (position + graphicsSize / 2)) / screenSize;
case 1:
return alignment - (2.0 * (position + graphicsSize)) / screenSize;
default:
throw new Error('Invalid graphics alignment.');
}
}
}
export { Frame, ResourceManager };