UNPKG

camstreamerlib

Version:

Helper library for CamStreamer ACAP applications.

167 lines (166 loc) 6.18 kB
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 };