camstreamerlib 3.2.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,50 +1,74 @@
1
- import { CamOverlayDrawingAPI, TAlign, TUploadImageResponse } from '../CamOverlayDrawingAPI';
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from 'events';
3
+ import { CamOverlayDrawingAPI, TAlign, TCairoCreateResponse, TUploadImageResponse } from '../CamOverlayDrawingAPI';
2
4
  import ResourceManager from './ResourceManager';
3
5
  export type TRgb = [number, number, number];
4
6
  export type TRgba = [number, number, number, number];
5
7
  export type TTmf = 'TFM_OVERFLOW' | 'TFM_SCALE' | 'TFM_TRUNCATE';
6
8
  export type TObjectFitType = 'fill' | 'fit' | 'none';
7
- export type FrameOptions = {
9
+ export type TFrameOptions = {
10
+ enabled?: boolean;
8
11
  x: number;
9
12
  y: number;
10
13
  width: number;
11
14
  height: number;
12
- enabled?: boolean;
13
- bgImage?: string;
14
15
  text?: string;
15
16
  fontColor?: TRgb;
17
+ font?: string;
16
18
  bgColor?: TRgba;
19
+ bgImage?: string;
17
20
  bgType?: TObjectFitType;
21
+ borderRadius?: number;
22
+ borderWidth?: number;
23
+ borderColor?: TRgba;
24
+ customDraw?: TDrawingCallback;
25
+ layer?: number;
18
26
  };
19
27
  export type TFrameInfo = {
20
28
  width: number;
21
29
  height: number;
22
30
  };
23
31
  export type TDrawingCallback = (cod: CamOverlayDrawingAPI, cairo: string, info: TFrameInfo) => Promise<void>;
24
- export default class Frame {
25
- private customDraw?;
32
+ export interface Frame {
33
+ on(event: 'layoutChanged', listener: () => void): this;
34
+ emit(event: 'layoutChanged'): boolean;
35
+ }
36
+ export declare class Frame extends EventEmitter {
37
+ protected enabled: boolean;
26
38
  protected posX: number;
27
39
  protected posY: number;
28
40
  protected width: number;
29
41
  protected height: number;
30
- protected enabled: boolean;
31
42
  private text;
43
+ private fontColor;
44
+ private font?;
32
45
  private align;
33
46
  private textType;
34
- private fontColor;
35
- private fontName?;
36
47
  private bgColor?;
37
48
  private bgImage?;
38
49
  private bgType?;
50
+ private borderRadius;
51
+ private borderWidth;
52
+ private borderColor;
53
+ private customDraw?;
54
+ private layer;
39
55
  protected children: Frame[];
40
- constructor(opt: FrameOptions, customDraw?: TDrawingCallback | undefined);
56
+ constructor(opt: TFrameOptions);
57
+ enable(): void;
58
+ disable(): void;
41
59
  setFramePosition(x: number, y: number): void;
42
60
  setFrameSize(width: number, height: number): void;
43
- setText(text: string, align: TAlign, textType?: TTmf, color?: TRgb): void;
61
+ setText(text: string, align: TAlign, textType?: TTmf, fontColor?: TRgb): void;
62
+ setFontColor(fontColor: TRgb): void;
44
63
  setFont(fontName: string): void;
64
+ setFontData(fontData: TCairoCreateResponse): void;
45
65
  setBgColor(color: TRgba): void;
46
66
  setBgImage(imageName: string, type?: TObjectFitType): void;
47
67
  setBgImageData(imageData: TUploadImageResponse, type?: TObjectFitType): void;
68
+ setBgType(type: TObjectFitType): void;
69
+ setBorderRadius(radius: number): void;
70
+ setBorderWidth(width: number): void;
71
+ setBorderColor(color: TRgba): void;
48
72
  setCustomDraw(customDraw: TDrawingCallback): void;
49
73
  resetFont(): void;
50
74
  resetBgColor(): void;
@@ -52,12 +76,15 @@ export default class Frame {
52
76
  resetCustomDraw(): void;
53
77
  clear(): void;
54
78
  insert(...frames: Frame[]): void;
55
- enable(): void;
56
- disable(): void;
57
- displayImage(cod: CamOverlayDrawingAPI, rm: ResourceManager, cairo: string, ppX: number, ppY: number, scale?: number): Promise<void>;
58
- protected displayOwnImage(cod: CamOverlayDrawingAPI, rm: ResourceManager, cairo: string, ppX: number, ppY: number, scale: number): Promise<void>;
79
+ getLayers(): Set<number>;
80
+ protected layoutChanged(): void;
81
+ displayImage(cod: CamOverlayDrawingAPI, resourceManager: ResourceManager, cairo: string, ppX: number, ppY: number, scale: number, layer: number): Promise<void>;
82
+ private prepareResources;
83
+ protected displayOwnImage(cod: CamOverlayDrawingAPI, cairo: string, ppX: number, ppY: number, scale: number): Promise<void>;
84
+ private clipDrawing;
59
85
  private drawFrame;
60
86
  private drawImage;
61
87
  private drawText;
88
+ private drawBorder;
89
+ private drawRectPath;
62
90
  }
63
- export { Frame };
@@ -10,24 +10,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Frame = void 0;
13
- class Frame {
14
- constructor(opt, customDraw) {
15
- var _a, _b, _c;
16
- this.customDraw = customDraw;
13
+ const events_1 = require("events");
14
+ class Frame extends events_1.EventEmitter {
15
+ constructor(opt) {
16
+ var _a, _b, _c, _d, _e, _f, _g;
17
+ super();
17
18
  this.text = '';
18
19
  this.align = 'A_LEFT';
19
20
  this.textType = 'TFM_OVERFLOW';
20
21
  this.children = new Array();
22
+ this.enabled = (_a = opt.enabled) !== null && _a !== void 0 ? _a : true;
21
23
  this.posX = opt.x;
22
24
  this.posY = opt.y;
23
25
  this.width = opt.width;
24
26
  this.height = opt.height;
25
- this.enabled = (_a = opt.enabled) !== null && _a !== void 0 ? _a : true;
26
27
  this.setText((_b = opt.text) !== null && _b !== void 0 ? _b : '', 'A_LEFT');
27
28
  this.fontColor = (_c = opt.fontColor) !== null && _c !== void 0 ? _c : [1.0, 1.0, 1.0];
29
+ this.font = opt.font;
28
30
  this.bgColor = opt.bgColor;
29
31
  this.bgImage = opt.bgImage;
30
32
  this.bgType = opt.bgType;
33
+ this.borderRadius = (_d = opt.borderRadius) !== null && _d !== void 0 ? _d : 0;
34
+ this.borderWidth = (_e = opt.borderWidth) !== null && _e !== void 0 ? _e : 0;
35
+ this.borderColor = (_f = opt.borderColor) !== null && _f !== void 0 ? _f : [1, 1, 1, 1];
36
+ this.customDraw = opt.customDraw;
37
+ this.layer = (_g = opt.layer) !== null && _g !== void 0 ? _g : 0;
38
+ }
39
+ enable() {
40
+ this.enabled = true;
41
+ }
42
+ disable() {
43
+ this.enabled = false;
31
44
  }
32
45
  setFramePosition(x, y) {
33
46
  this.posX = x;
@@ -37,16 +50,22 @@ class Frame {
37
50
  this.width = width;
38
51
  this.height = height;
39
52
  }
40
- setText(text, align, textType = 'TFM_OVERFLOW', color) {
53
+ setText(text, align, textType = 'TFM_OVERFLOW', fontColor) {
41
54
  this.text = text;
42
55
  this.align = align;
43
56
  this.textType = textType;
44
- if (color) {
45
- this.fontColor = color;
57
+ if (fontColor) {
58
+ this.fontColor = fontColor;
46
59
  }
47
60
  }
61
+ setFontColor(fontColor) {
62
+ this.fontColor = fontColor;
63
+ }
48
64
  setFont(fontName) {
49
- this.fontName = fontName;
65
+ this.font = fontName;
66
+ }
67
+ setFontData(fontData) {
68
+ this.font = fontData;
50
69
  }
51
70
  setBgColor(color) {
52
71
  this.bgColor = color;
@@ -59,11 +78,23 @@ class Frame {
59
78
  this.bgImage = imageData;
60
79
  this.bgType = type;
61
80
  }
81
+ setBgType(type) {
82
+ this.bgType = type;
83
+ }
84
+ setBorderRadius(radius) {
85
+ this.borderRadius = radius;
86
+ }
87
+ setBorderWidth(width) {
88
+ this.borderWidth = width;
89
+ }
90
+ setBorderColor(color) {
91
+ this.borderColor = color;
92
+ }
62
93
  setCustomDraw(customDraw) {
63
94
  this.customDraw = customDraw;
64
95
  }
65
96
  resetFont() {
66
- this.fontName = undefined;
97
+ this.font = undefined;
67
98
  }
68
99
  resetBgColor() {
69
100
  this.bgColor = undefined;
@@ -80,7 +111,7 @@ class Frame {
80
111
  this.align = 'A_LEFT';
81
112
  this.textType = 'TFM_OVERFLOW';
82
113
  this.fontColor = [1.0, 1.0, 1.0];
83
- this.fontName = undefined;
114
+ this.font = undefined;
84
115
  this.bgColor = undefined;
85
116
  this.bgImage = undefined;
86
117
  this.bgType = undefined;
@@ -88,55 +119,96 @@ class Frame {
88
119
  }
89
120
  insert(...frames) {
90
121
  this.children.push(...frames);
122
+ for (const frame of frames) {
123
+ frame.on('layoutChanged', () => this.layoutChanged());
124
+ }
125
+ this.layoutChanged();
91
126
  }
92
- enable() {
93
- this.enabled = true;
127
+ getLayers() {
128
+ const uniqueLayers = new Set();
129
+ uniqueLayers.add(this.layer);
130
+ for (const child of this.children) {
131
+ for (const layer of child.getLayers()) {
132
+ uniqueLayers.add(layer);
133
+ }
134
+ }
135
+ return uniqueLayers;
94
136
  }
95
- disable() {
96
- this.enabled = false;
137
+ layoutChanged() {
138
+ this.emit('layoutChanged');
97
139
  }
98
- displayImage(cod, rm, cairo, ppX, ppY, scale = 1) {
140
+ displayImage(cod, resourceManager, cairo, ppX, ppY, scale, layer) {
99
141
  return __awaiter(this, void 0, void 0, function* () {
100
142
  if (this.enabled) {
101
143
  ppX += this.posX;
102
144
  ppY += this.posY;
103
- yield this.displayOwnImage(cod, rm, cairo, ppX, ppY, scale);
145
+ yield this.prepareResources(resourceManager);
146
+ yield cod.cairo('cairo_save', cairo);
147
+ yield this.clipDrawing(cod, cairo, scale, ppX, ppY);
148
+ if (this.layer === layer) {
149
+ yield this.displayOwnImage(cod, cairo, ppX, ppY, scale);
150
+ }
104
151
  for (const child of this.children) {
105
- yield child.displayImage(cod, rm, cairo, ppX, ppY, scale);
152
+ yield child.displayImage(cod, resourceManager, cairo, ppX, ppY, scale, layer);
106
153
  }
154
+ yield cod.cairo('cairo_restore', cairo);
107
155
  }
108
156
  });
109
157
  }
110
- displayOwnImage(cod, rm, cairo, ppX, ppY, scale) {
158
+ prepareResources(resourceManager) {
111
159
  return __awaiter(this, void 0, void 0, function* () {
112
- if (this.enabled) {
113
- const promises = new Array();
114
- if (this.fontName !== undefined) {
115
- const fontData = yield rm.font(this.fontName);
116
- promises.push(cod.cairo('cairo_set_font_face', cairo, fontData.var));
117
- }
118
- else {
119
- promises.push(cod.cairo('cairo_set_font_face', cairo, 'NULL'));
120
- }
121
- if (this.bgColor !== undefined) {
122
- promises.push(this.drawFrame(cod, cairo, scale, ppX, ppY));
123
- }
124
- if (this.bgImage !== undefined) {
125
- promises.push(this.drawImage(cod, rm, cairo, scale, ppX, ppY));
126
- }
127
- if (this.text) {
128
- promises.push(this.drawText(cod, cairo, scale, ppX, ppY));
129
- }
130
- if (this.customDraw) {
131
- promises.push(cod.cairo('cairo_identity_matrix', cairo));
132
- promises.push(cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY));
133
- promises.push(cod.cairo('cairo_scale', cairo, scale, scale));
134
- promises.push(this.customDraw(cod, cairo, { width: this.width, height: this.height }));
135
- }
136
- yield Promise.all(promises);
160
+ if (typeof this.bgImage === 'string') {
161
+ this.bgImage = yield resourceManager.image(this.bgImage);
162
+ }
163
+ if (typeof this.font === 'string') {
164
+ this.font = yield resourceManager.font(this.font);
137
165
  }
138
166
  });
139
167
  }
168
+ displayOwnImage(cod, cairo, ppX, ppY, scale) {
169
+ return __awaiter(this, void 0, void 0, function* () {
170
+ if (!this.enabled) {
171
+ return;
172
+ }
173
+ const promises = new Array();
174
+ if (this.font !== undefined && typeof this.font !== 'string') {
175
+ promises.push(cod.cairo('cairo_set_font_face', cairo, this.font.var));
176
+ }
177
+ else {
178
+ promises.push(cod.cairo('cairo_set_font_face', cairo, 'NULL'));
179
+ }
180
+ if (this.bgColor !== undefined) {
181
+ promises.push(this.drawFrame(cod, cairo, scale, ppX, ppY));
182
+ }
183
+ if (this.bgImage !== undefined) {
184
+ promises.push(this.drawImage(cod, cairo, scale, ppX, ppY));
185
+ }
186
+ if (this.borderWidth > 0) {
187
+ promises.push(this.drawBorder(cod, cairo, scale, ppX, ppY));
188
+ }
189
+ if (this.text) {
190
+ promises.push(this.drawText(cod, cairo, scale, ppX, ppY));
191
+ }
192
+ if (this.customDraw) {
193
+ promises.push(cod.cairo('cairo_identity_matrix', cairo));
194
+ promises.push(cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY));
195
+ promises.push(cod.cairo('cairo_scale', cairo, scale, scale));
196
+ promises.push(this.customDraw(cod, cairo, { width: this.width, height: this.height }));
197
+ }
198
+ yield Promise.all(promises);
199
+ });
200
+ }
201
+ clipDrawing(cod, cairo, scale, ppX, ppY) {
202
+ return __awaiter(this, void 0, void 0, function* () {
203
+ if (this.borderRadius === 0) {
204
+ return;
205
+ }
206
+ yield Promise.all([
207
+ this.drawRectPath(cod, cairo, scale, ppX, ppY, this.borderRadius),
208
+ cod.cairo('cairo_clip', cairo),
209
+ ]);
210
+ });
211
+ }
140
212
  drawFrame(cod, cairo, scale, ppX, ppY) {
141
213
  return __awaiter(this, void 0, void 0, function* () {
142
214
  if (this.bgColor) {
@@ -152,16 +224,18 @@ class Frame {
152
224
  yield Promise.all(promises);
153
225
  }
154
226
  else {
155
- throw new Error('Colour of this frame is undefined.');
227
+ throw new Error('Color of the frame is undefined.');
156
228
  }
157
229
  });
158
230
  }
159
- drawImage(cod, rm, cairo, scale, ppX, ppY) {
231
+ drawImage(cod, cairo, scale, ppX, ppY) {
160
232
  return __awaiter(this, void 0, void 0, function* () {
161
- const imageData = typeof this.bgImage === 'string' ? yield rm.image(this.bgImage) : this.bgImage;
162
- const bgImage = imageData.var;
163
- const bgWidth = imageData.width;
164
- const bgHeight = imageData.height;
233
+ if (this.bgImage === undefined || typeof this.bgImage === 'string') {
234
+ return;
235
+ }
236
+ const bgImage = this.bgImage.var;
237
+ const bgWidth = this.bgImage.width;
238
+ const bgHeight = this.bgImage.height;
165
239
  const promises = new Array();
166
240
  promises.push(cod.cairo('cairo_identity_matrix', cairo));
167
241
  promises.push(cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY));
@@ -194,6 +268,41 @@ class Frame {
194
268
  yield Promise.all(promises);
195
269
  });
196
270
  }
271
+ drawBorder(cod, cairo, scale, ppX, ppY) {
272
+ return __awaiter(this, void 0, void 0, function* () {
273
+ yield Promise.all([
274
+ this.drawRectPath(cod, cairo, scale, ppX, ppY, this.borderRadius),
275
+ cod.cairo('cairo_set_source_rgba', cairo, this.borderColor[0], this.borderColor[1], this.borderColor[2], this.borderColor[3]),
276
+ cod.cairo('cairo_set_line_width', cairo, this.borderWidth),
277
+ cod.cairo('cairo_stroke', cairo),
278
+ ]);
279
+ });
280
+ }
281
+ drawRectPath(cod, cairo, scale, ppX, ppY, radius) {
282
+ return __awaiter(this, void 0, void 0, function* () {
283
+ if (radius === 0) {
284
+ return yield Promise.all([
285
+ cod.cairo('cairo_identity_matrix', cairo),
286
+ cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY),
287
+ cod.cairo('cairo_scale', cairo, scale, scale),
288
+ cod.cairo('cairo_rectangle', cairo, 0, 0, this.width, this.height),
289
+ ]);
290
+ }
291
+ else {
292
+ const degrees = Math.PI / 180;
293
+ return yield Promise.all([
294
+ cod.cairo('cairo_identity_matrix', cairo),
295
+ cod.cairo('cairo_translate', cairo, scale * ppX, scale * ppY),
296
+ cod.cairo('cairo_scale', cairo, scale, scale),
297
+ cod.cairo('cairo_new_sub_path', cairo),
298
+ cod.cairo('cairo_arc', cairo, this.width - radius, radius, radius, -90 * degrees, 0 * degrees),
299
+ cod.cairo('cairo_arc', cairo, this.width - radius, this.height - radius, radius, 0 * degrees, 90 * degrees),
300
+ cod.cairo('cairo_arc', cairo, radius, this.height - radius, radius, 90 * degrees, 180 * degrees),
301
+ cod.cairo('cairo_arc', cairo, radius, radius, radius, 180 * degrees, 270 * degrees),
302
+ cod.cairo('cairo_close_path', cairo),
303
+ ]);
304
+ }
305
+ });
306
+ }
197
307
  }
198
- exports.default = Frame;
199
308
  exports.Frame = Frame;
@@ -1,34 +1,37 @@
1
1
  import { CamOverlayDrawingAPI, CamOverlayDrawingOptions } from '../CamOverlayDrawingAPI';
2
2
  import ResourceManager from './ResourceManager';
3
- import { Frame, FrameOptions } from './Frame';
3
+ import { Frame, TFrameOptions } from './Frame';
4
4
  export declare const COORD: Record<string, [number, number]>;
5
- export type PainterOptions = FrameOptions & {
5
+ export type TPainterOptions = TFrameOptions & {
6
6
  screenWidth: number;
7
7
  screenHeight: number;
8
8
  coAlignment: string;
9
9
  };
10
- export default class Painter extends Frame {
10
+ export declare class Painter extends Frame {
11
11
  private screenWidth;
12
12
  private screenHeight;
13
13
  private coAlignment;
14
- private surface?;
15
- private cairo?;
16
14
  private cod;
17
15
  private rm;
16
+ private refreshLayers;
17
+ private layers;
18
+ constructor(opt: TPainterOptions, coopt: CamOverlayDrawingOptions);
18
19
  get camOverlayDrawingAPI(): CamOverlayDrawingAPI;
19
20
  get resourceManager(): ResourceManager;
20
- constructor(opt: PainterOptions, coopt: CamOverlayDrawingOptions);
21
21
  connect(): void;
22
22
  disconnect(): void;
23
23
  isConnected(): boolean;
24
24
  registerImage(moniker: string, fileName: string): void;
25
25
  registerFont(moniker: string, fileName: string): void;
26
26
  setScreenSize(sw: number, sh: number): void;
27
- setCoAlignment(coa: string): void;
27
+ setCoAlignment(coAlignment: string): void;
28
+ protected layoutChanged(): void;
28
29
  display(scale?: number): Promise<void>;
29
30
  hide(): Promise<void>;
31
+ invalidateLayer(layer: number): Promise<void>;
32
+ private prepareLayers;
33
+ private prepareSurface;
34
+ private cleanupSurface;
30
35
  private positionConvertor;
31
- private prepareDrawing;
32
- private destroy;
33
36
  }
34
- export { Painter, Frame, FrameOptions, ResourceManager, CamOverlayDrawingOptions };
37
+ export { Frame, TFrameOptions as FrameOptions, ResourceManager, CamOverlayDrawingOptions };
@@ -27,20 +27,22 @@ exports.COORD = {
27
27
  bottom_right: [1, 1],
28
28
  };
29
29
  class Painter extends Frame_1.Frame {
30
- get camOverlayDrawingAPI() {
31
- return this.cod;
32
- }
33
- get resourceManager() {
34
- return this.rm;
35
- }
36
30
  constructor(opt, coopt) {
37
31
  super(opt);
32
+ this.refreshLayers = true;
33
+ this.layers = [];
38
34
  this.coAlignment = exports.COORD[opt.coAlignment];
39
35
  this.screenWidth = opt.screenWidth;
40
36
  this.screenHeight = opt.screenHeight;
41
37
  this.cod = new CamOverlayDrawingAPI_1.CamOverlayDrawingAPI(coopt);
42
38
  this.rm = new ResourceManager_1.default(this.cod);
43
39
  }
40
+ get camOverlayDrawingAPI() {
41
+ return this.cod;
42
+ }
43
+ get resourceManager() {
44
+ return this.rm;
45
+ }
44
46
  connect() {
45
47
  this.cod.on('open', () => {
46
48
  this.rm.clear();
@@ -66,19 +68,45 @@ class Painter extends Frame_1.Frame {
66
68
  this.screenWidth = sw;
67
69
  this.screenHeight = sh;
68
70
  }
69
- setCoAlignment(coa) {
70
- this.coAlignment = exports.COORD[coa];
71
+ setCoAlignment(coAlignment) {
72
+ this.coAlignment = exports.COORD[coAlignment];
71
73
  }
72
- display(scale = 1) {
74
+ layoutChanged() {
75
+ this.refreshLayers = true;
76
+ }
77
+ display(scale = 1.0) {
73
78
  return __awaiter(this, void 0, void 0, function* () {
74
79
  if (this.enabled) {
75
- [this.surface, this.cairo] = yield this.prepareDrawing(scale);
76
- yield this.displayOwnImage(this.cod, this.rm, this.cairo, 0, 0, scale);
77
- for (const child of this.children) {
78
- yield child.displayImage(this.cod, this.rm, this.cairo, 0, 0, scale);
80
+ if (this.refreshLayers) {
81
+ this.refreshLayers = false;
82
+ yield this.prepareLayers();
83
+ }
84
+ let cairo;
85
+ let surface;
86
+ let lastCachedLayer;
87
+ for (let i = 0; i < this.layers.length; i++) {
88
+ const layer = this.layers[i];
89
+ if (layer.cairoCache !== undefined &&
90
+ layer.surfaceCache !== undefined &&
91
+ surface === undefined &&
92
+ cairo === undefined) {
93
+ lastCachedLayer = layer;
94
+ continue;
95
+ }
96
+ if (surface === undefined || cairo === undefined) {
97
+ [surface, cairo] = yield this.prepareSurface(scale, lastCachedLayer === null || lastCachedLayer === void 0 ? void 0 : lastCachedLayer.surfaceCache, lastCachedLayer === null || lastCachedLayer === void 0 ? void 0 : lastCachedLayer.cairoCache);
98
+ }
99
+ yield this.displayImage(this.cod, this.rm, cairo, 0, 0, scale, layer.layer);
100
+ if (i < this.layers.length - 1) {
101
+ const [surfaceToCache, cairoToCache] = yield this.prepareSurface(scale, surface, cairo);
102
+ layer.surfaceCache = surfaceToCache;
103
+ layer.cairoCache = cairoToCache;
104
+ }
105
+ }
106
+ if (surface !== undefined && cairo !== undefined) {
107
+ yield 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));
108
+ yield this.cleanupSurface(surface, cairo);
79
109
  }
80
- yield this.cod.showCairoImage(this.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));
81
- yield this.destroy();
82
110
  }
83
111
  });
84
112
  }
@@ -87,6 +115,53 @@ class Painter extends Frame_1.Frame {
87
115
  yield this.cod.removeImage();
88
116
  });
89
117
  }
118
+ invalidateLayer(layer) {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ const layerIdx = this.layers.findIndex((l) => l.layer === layer);
121
+ if (layerIdx === -1) {
122
+ return;
123
+ }
124
+ for (let i = layerIdx; i < this.layers.length; i++) {
125
+ const layer = this.layers[i];
126
+ if (layer.surfaceCache !== undefined && layer.cairoCache !== undefined) {
127
+ yield this.cleanupSurface(layer.surfaceCache, layer.cairoCache);
128
+ layer.surfaceCache = undefined;
129
+ layer.cairoCache = undefined;
130
+ }
131
+ }
132
+ });
133
+ }
134
+ prepareLayers() {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ for (const layer of this.layers) {
137
+ if (layer.surfaceCache !== undefined && layer.cairoCache !== undefined) {
138
+ yield this.cleanupSurface(layer.surfaceCache, layer.cairoCache);
139
+ }
140
+ }
141
+ const uniqueLayers = this.getLayers();
142
+ const sortedLayers = Array.from(uniqueLayers).sort((a, b) => a - b);
143
+ this.layers = sortedLayers.map((layer) => {
144
+ return { layer };
145
+ });
146
+ });
147
+ }
148
+ prepareSurface(scale, cachedSurface, cachedCairo) {
149
+ return __awaiter(this, void 0, void 0, function* () {
150
+ const surface = (yield this.cod.cairo('cairo_image_surface_create', 'CAIRO_FORMAT_ARGB32', Math.floor(this.width * scale), Math.floor(this.height * scale)));
151
+ const cairo = (yield this.cod.cairo('cairo_create', surface.var));
152
+ if (cachedSurface !== undefined && cachedCairo !== undefined) {
153
+ yield this.cod.cairo('cairo_set_source_surface', cairo.var, cachedSurface, 0, 0);
154
+ yield this.cod.cairo('cairo_paint', cairo.var);
155
+ }
156
+ return [surface.var, cairo.var];
157
+ });
158
+ }
159
+ cleanupSurface(surface, cairo) {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ yield this.cod.cairo('cairo_surface_destroy', surface);
162
+ yield this.cod.cairo('cairo_destroy', cairo);
163
+ });
164
+ }
90
165
  positionConvertor(alignment, screenSize, position, graphicsSize) {
91
166
  switch (alignment) {
92
167
  case -1:
@@ -99,19 +174,5 @@ class Painter extends Frame_1.Frame {
99
174
  throw new Error('Invalid graphics alignment.');
100
175
  }
101
176
  }
102
- prepareDrawing(scale) {
103
- return __awaiter(this, void 0, void 0, function* () {
104
- const surface = (yield this.cod.cairo('cairo_image_surface_create', 'CAIRO_FORMAT_ARGB32', Math.floor(this.width * scale), Math.floor(this.height * scale)));
105
- const cairo = (yield this.cod.cairo('cairo_create', surface.var));
106
- return [surface.var, cairo.var];
107
- });
108
- }
109
- destroy() {
110
- return __awaiter(this, void 0, void 0, function* () {
111
- yield this.cod.cairo('cairo_surface_destroy', this.surface);
112
- yield this.cod.cairo('cairo_destroy', this.cairo);
113
- });
114
- }
115
177
  }
116
- exports.default = Painter;
117
178
  exports.Painter = Painter;
package/CameraVapix.d.ts CHANGED
@@ -78,13 +78,13 @@ export declare class CameraVapix {
78
78
  getTimezone(): Promise<string>;
79
79
  getHeaders(): Promise<Record<string, string>>;
80
80
  setHeaders(headers: Record<string, string>): Promise<Response>;
81
- private parseGetParametrs;
81
+ private parseParameters;
82
82
  getParameterGroup(groupNames: string): Promise<Record<string, string>>;
83
83
  setParameter(params: Record<string, string>): Promise<Response>;
84
84
  getGuardTourList(): Promise<TGuardTour[]>;
85
- setGuardTourEnabled(gourTourID: string, enable: boolean): Promise<Response>;
85
+ setGuardTourEnabled(guardTourID: string, enable: boolean): Promise<Response>;
86
86
  private parsePtz;
87
- private parseCameraPtzFromReq;
87
+ private parseCameraPtzResponse;
88
88
  getPTZPresetList(channel: number): Promise<string[]>;
89
89
  listPtzVideoSourceOverview(): Promise<TPtzOverview>;
90
90
  goToPreset(channel: number, presetName: string): Promise<Response>;
package/CameraVapix.js CHANGED
@@ -46,8 +46,7 @@ class CameraVapix {
46
46
  }
47
47
  getCameraImage(camera, compression, resolution, outputStream) {
48
48
  return __awaiter(this, void 0, void 0, function* () {
49
- const path = `/axis-cgi/jpg/image.cgi?resolution=${resolution}&compression=${compression}&camera=${camera}`;
50
- const res = yield this.vapixGet(path);
49
+ const res = yield this.vapixGet('/axis-cgi/jpg/image.cgi', { resolution, compression, camera });
51
50
  if (res.body) {
52
51
  void res.body.pipeTo(outputStream);
53
52
  }
@@ -181,40 +180,26 @@ class CameraVapix {
181
180
  return this.vapixPost('/axis-cgi/customhttpheader.cgi', data);
182
181
  });
183
182
  }
184
- parseGetParametrs(response) {
183
+ parseParameters(response) {
185
184
  const params = {};
186
185
  const lines = response.split(/[\r\n]/);
187
186
  for (let i = 0; i < lines.length; i++) {
188
- if (lines[i].substring(0, 7) === '# Error') {
187
+ if (lines[i].length === 0 || lines[i].substring(0, 7) === '# Error') {
189
188
  continue;
190
189
  }
191
- if (lines[i].length) {
192
- const p = lines[i].split(/=(.+)/);
193
- if (p.length >= 2) {
194
- const paramName = p[0].replace('root.', '');
195
- params[paramName] = String(p[1]);
196
- }
197
- else if (p[0].substring(0, 7) !== '# Error') {
198
- params[p[0].slice(0, -1)] = '';
199
- }
190
+ const delimiterPos = lines[i].indexOf('=');
191
+ if (delimiterPos !== -1) {
192
+ const paramName = lines[i].substring(0, delimiterPos);
193
+ const paramValue = lines[i].substring(delimiterPos + 1);
194
+ params[paramName] = paramValue;
200
195
  }
201
196
  }
202
197
  return params;
203
198
  }
204
199
  getParameterGroup(groupNames) {
205
200
  return __awaiter(this, void 0, void 0, function* () {
206
- const response = yield (yield this.vapixGet(`/axis-cgi/param.cgi?action=list&group=${encodeURIComponent(groupNames)}`)).text();
207
- const params = {};
208
- const lines = response.split(/[\r\n]/);
209
- for (let i = 0; i < lines.length; i++) {
210
- if (lines[i].length) {
211
- const p = lines[i].split('=');
212
- if (p.length >= 2) {
213
- params[p[0]] = p[1];
214
- }
215
- }
216
- }
217
- return params;
201
+ const response = yield this.vapixGet('/axis-cgi/param.cgi', { action: 'list', group: groupNames });
202
+ return this.parseParameters(yield response.text());
218
203
  });
219
204
  }
220
205
  setParameter(params) {
@@ -263,26 +248,26 @@ class CameraVapix {
263
248
  return gTourList;
264
249
  });
265
250
  }
266
- setGuardTourEnabled(gourTourID, enable) {
251
+ setGuardTourEnabled(guardTourID, enable) {
267
252
  const options = {};
268
- options[gourTourID + '.Running'] = enable ? 'yes' : 'no';
253
+ options[guardTourID + '.Running'] = enable ? 'yes' : 'no';
269
254
  return this.setParameter(options);
270
255
  }
271
256
  parsePtz(parsed) {
272
257
  const res = [];
273
258
  parsed.forEach((value) => {
274
- const valueData = value.split('=');
275
- if (valueData.length < 2) {
259
+ const delimiterPos = value.indexOf('=');
260
+ if (delimiterPos === -1) {
276
261
  return;
277
262
  }
278
- if (!valueData[0].startsWith('presetposno')) {
263
+ if (!value.startsWith('presetposno')) {
279
264
  return;
280
265
  }
281
- const id = Number(valueData[0].replace('presetposno', ''));
266
+ const id = Number(value.substring(11, delimiterPos));
282
267
  if (Number.isNaN(id)) {
283
268
  return;
284
269
  }
285
- const data = valueData[1].split(':');
270
+ const data = value.substring(delimiterPos + 1).split(':');
286
271
  const getValue = (valueName) => {
287
272
  for (const d of data) {
288
273
  const p = d.split('=');
@@ -304,7 +289,7 @@ class CameraVapix {
304
289
  });
305
290
  return res;
306
291
  }
307
- parseCameraPtzFromReq(response) {
292
+ parseCameraPtzResponse(response) {
308
293
  const json = JSON.parse(response);
309
294
  const parsed = {};
310
295
  Object.keys(json).forEach((key) => {
@@ -320,7 +305,7 @@ class CameraVapix {
320
305
  }
321
306
  getPTZPresetList(channel) {
322
307
  return __awaiter(this, void 0, void 0, function* () {
323
- const response = yield (yield this.vapixGet(`/axis-cgi/com/ptz.cgi?query=presetposcam&camera=${encodeURIComponent(channel)}`)).text();
308
+ const response = yield (yield this.vapixGet('/axis-cgi/com/ptz.cgi', { query: 'presetposcam', camera: channel.toString() })).text();
324
309
  const positions = [];
325
310
  const lines = response.split(/[\r\n]/);
326
311
  for (const line of lines) {
@@ -338,11 +323,11 @@ class CameraVapix {
338
323
  listPtzVideoSourceOverview() {
339
324
  return __awaiter(this, void 0, void 0, function* () {
340
325
  try {
341
- const response = yield this.vapixGet(`/axis-cgi/com/ptz.cgi`, {
326
+ const response = yield this.vapixGet('/axis-cgi/com/ptz.cgi', {
342
327
  query: 'presetposall',
343
328
  format: 'json',
344
329
  });
345
- const data = this.parseCameraPtzFromReq(yield response.text());
330
+ const data = this.parseCameraPtzResponse(yield response.text());
346
331
  const res = {};
347
332
  Object.keys(data).forEach((camera) => {
348
333
  res[Number(camera) - 1] = data[Number(camera)].map((_a) => {
@@ -358,40 +343,36 @@ class CameraVapix {
358
343
  });
359
344
  }
360
345
  goToPreset(channel, presetName) {
361
- return this.vapixPost('/axis-cgi/com/ptz.cgi', `camera=${encodeURIComponent(channel)}&gotoserverpresetname=${encodeURIComponent(presetName)}`);
346
+ return this.vapixGet('/axis-cgi/com/ptz.cgi', { camera: channel.toString(), gotoserverpresetname: presetName });
362
347
  }
363
348
  getPtzPosition(camera) {
364
349
  return __awaiter(this, void 0, void 0, function* () {
365
350
  try {
366
- const res = yield this.vapixGet(`/axis-cgi/com/ptz.cgi`, {
351
+ const res = yield this.vapixGet('/axis-cgi/com/ptz.cgi', {
367
352
  query: 'position',
368
353
  camera: camera.toString(),
369
354
  });
370
- const data = this.parseGetParametrs(yield res.text());
355
+ const params = this.parseParameters(yield res.text());
371
356
  return {
372
- pan: Number(data.pan),
373
- tilt: Number(data.tilt),
374
- zoom: Number(data.zoom),
357
+ pan: Number(params.pan),
358
+ tilt: Number(params.tilt),
359
+ zoom: Number(params.zoom),
375
360
  };
376
361
  }
377
362
  catch (err) {
378
- return {
379
- pan: 0,
380
- tilt: 0,
381
- zoom: 0,
382
- };
363
+ return { pan: 0, tilt: 0, zoom: 0 };
383
364
  }
384
365
  });
385
366
  }
386
367
  getInputState(port) {
387
368
  return __awaiter(this, void 0, void 0, function* () {
388
- const response = yield (yield this.vapixPost('/axis-cgi/io/port.cgi', `checkactive=${encodeURIComponent(port)}`)).text();
369
+ const response = yield (yield this.vapixGet('/axis-cgi/io/port.cgi', { checkactive: port.toString() })).text();
389
370
  return response.split('=')[1].indexOf('active') === 0;
390
371
  });
391
372
  }
392
373
  setOutputState(port, active) {
393
374
  return __awaiter(this, void 0, void 0, function* () {
394
- return this.vapixPost('/axis-cgi/io/port.cgi', `action=${encodeURIComponent(port)}:${active ? '/' : '\\'}`);
375
+ return this.vapixGet('/axis-cgi/io/port.cgi', { action: active ? `${port}:/` : `${port}:\\` });
395
376
  });
396
377
  }
397
378
  getApplicationList() {
package/DefaultAgent.js CHANGED
@@ -50,7 +50,7 @@ class DefaultAgent {
50
50
  path += '&';
51
51
  }
52
52
  for (const key in params) {
53
- path += `${key}=${params[key]}&`;
53
+ path += `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}&`;
54
54
  }
55
55
  path = path.slice(0, path.length - 1);
56
56
  return {
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Node.js helper library for CamStreamer ACAP applications.
4
4
 
5
- The library is primarily developed for CamScripter Acap application running directly in Axis cameras.
5
+ The library is primarily developed for the CamScripter ACAP application running directly in Axis cameras.
6
6
  Examples of CamScripter packages can be found at https://github.com/CamStreamer/CamScripterApp_examples
7
7
 
8
8
  ## Installation
@@ -30,16 +30,15 @@ class HttpRequestSender {
30
30
  return this.sendRequestWithAuth(options, postData);
31
31
  }
32
32
  sendRequestWithAuth(options, postData, wwwAuthenticateHeader) {
33
- var _a;
33
+ var _a, _b;
34
34
  return __awaiter(this, void 0, void 0, function* () {
35
+ (_a = options.timeout) !== null && _a !== void 0 ? _a : (options.timeout = 10000);
35
36
  const url = HttpRequestSender.getURL(options);
36
37
  const controller = new AbortController();
37
- if (options.timeout !== undefined) {
38
- setTimeout(() => controller.abort(new Error('Request timeout')), options.timeout);
39
- }
38
+ setTimeout(() => controller.abort(new Error('Request timeout')), options.timeout);
40
39
  const authorization = this.getAuthorization(options, wwwAuthenticateHeader);
41
40
  if (authorization !== undefined) {
42
- (_a = options.headers) !== null && _a !== void 0 ? _a : (options.headers = {});
41
+ (_b = options.headers) !== null && _b !== void 0 ? _b : (options.headers = {});
43
42
  options.headers['Authorization'] = authorization;
44
43
  }
45
44
  let res;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstreamerlib",
3
- "version": "3.2.3",
3
+ "version": "3.3.0",
4
4
  "description": "Helper library for CamStreamer ACAP applications.",
5
5
  "prettier": "@camstreamer/prettier-config",
6
6
  "dependencies": {