@wemap/camera 9.2.0 → 10.0.0-alpha.10

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.
@@ -0,0 +1,623 @@
1
+ import { BarcodeFormat, MultiFormatReader, DecodeHintType, BinaryBitmap, HybridBinarizer, RGBLuminanceSource } from '@zxing/library';
2
+ export { BarcodeFormat } from '@zxing/library';
3
+ import EventEmitter from 'events';
4
+ import Logger from '@wemap/logger';
5
+
6
+ class SharedCameras extends EventEmitter {
7
+
8
+ /**
9
+ * Singleton pattern.
10
+ * @returns {SharedCameras}
11
+ */
12
+ static get instance() {
13
+ if (!this._instance) {
14
+ this._instance = new SharedCameras();
15
+ }
16
+ return this._instance;
17
+ }
18
+
19
+ /** @type {{container: Node, camera: Camera}[]} */
20
+ _list = [];
21
+
22
+ /**
23
+ * @param {Camera} camera
24
+ * @param {Node}
25
+ */
26
+ _add(camera, container) {
27
+ const obj = {
28
+ camera,
29
+ container
30
+ };
31
+ this._list.push(obj);
32
+ this.emit('added', obj);
33
+ }
34
+
35
+ /**
36
+ * @param {Camera} camera
37
+ */
38
+ _remove(camera) {
39
+ this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);
40
+ this.emit('removed', { camera });
41
+ }
42
+
43
+ /**
44
+ * @returns {{container: Node, camera: Camera}[]}
45
+ */
46
+ get list() {
47
+ return this._list;
48
+ }
49
+
50
+ /**
51
+ * @param {Node} container
52
+ * @returns {?Camera}
53
+ */
54
+ getCameraByContainer(container) {
55
+ for (const {
56
+ camera, container: _container
57
+ } of this._list) {
58
+ if (container === _container) {
59
+ return camera;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+ }
65
+
66
+ var SharedCameras$1 = SharedCameras.instance;
67
+
68
+ /**
69
+ * @param {number} angle
70
+ * @param {number} aspectRatio
71
+ * @returns {number}
72
+ */
73
+ function convertFov(angle, aspectRatio) {
74
+ return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;
75
+ }
76
+
77
+ /**
78
+ * @param {HTMLDivElement} videoContainer
79
+ * @param {HTMLVideoElement} videoElement
80
+ * @param {number} hardwareVerticalFov
81
+ * @returns {object}
82
+ */
83
+ function calcDisplayedFov(videoContainer, videoElement, hardwareVerticalFov) {
84
+
85
+ const sourceWidth = videoElement.videoWidth;
86
+ const sourceHeight = videoElement.videoHeight;
87
+
88
+ const containerWidth = videoContainer.offsetWidth;
89
+ const containerHeight = videoContainer.offsetHeight;
90
+
91
+ const sourceAspect = sourceWidth / sourceHeight;
92
+ const screenAspect = containerWidth / containerHeight;
93
+
94
+ let fovV = hardwareVerticalFov;
95
+ let fovH = convertFov(fovV, sourceAspect);
96
+
97
+ if (screenAspect < sourceAspect) {
98
+ fovH = convertFov(fovV, screenAspect);
99
+ } else {
100
+ fovV = convertFov(fovH, 1 / screenAspect);
101
+ }
102
+
103
+ return {
104
+ vertical: fovV,
105
+ horizontal: fovH
106
+ };
107
+ }
108
+
109
+ /**
110
+ * @param {HTMLCanvasElement} canvas
111
+ * @param {string} type
112
+ * @param {any} quality
113
+ * @returns {string}
114
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
115
+ */
116
+ function canvasToBase64(canvas, type = 'image/png', quality = null) {
117
+ return canvas
118
+ .toDataURL(type, quality)
119
+ .substring(('data:' + type + ';base64,').length);
120
+ }
121
+
122
+ /**
123
+ * Creates a pinhole camera from dimensions and a field of view
124
+ * Distortions are not considered
125
+ *
126
+ * @param {number} width the camera width
127
+ * @param {number} height the camera height
128
+ * @param {number} fovOfWidth the field of view along the width axis in radians
129
+ * @returns {object} the calibration matrix
130
+ */
131
+ function createCameraCalibrationFromWidthHeightFov(width, height, fovOfWidth) {
132
+
133
+ const fovOfHeight = 2 * Math.atan(height * Math.tan(fovOfWidth / 2) / width);
134
+
135
+ const fx = width / (2 * Math.tan(0.5 * fovOfWidth));
136
+ const fy = height / (2 * Math.tan(0.5 * fovOfHeight));
137
+ const cx = width / 2;
138
+ const cy = height / 2;
139
+
140
+ return {
141
+ intrinsic: [fx, 0, cx, 0, fy, cy, 0, 0, 1],
142
+ distortions: [0, 0, 0, 0, 0]
143
+ };
144
+ }
145
+
146
+ /**
147
+ * @param {HTMLCanvasElement} imageCanvas
148
+ */
149
+ function convertToGrayscale(imageCanvas) {
150
+ const ctx = imageCanvas.getContext('2d');
151
+ const imgData = ctx.getImageData(0, 0, imageCanvas.width, imageCanvas.height);
152
+ const pixels = imgData.data;
153
+ for (let i = 0; i < pixels.length; i += 4) {
154
+
155
+ const lightness = parseInt((pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3, 10);
156
+ pixels[i] = lightness;
157
+ pixels[i + 1] = lightness;
158
+ pixels[i + 2] = lightness;
159
+ }
160
+ ctx.putImageData(imgData, 0, 0);
161
+ }
162
+
163
+ /**
164
+ * Reduce image size and keep aspect ratio
165
+ * @param {HTMLCanvasElement} imageCanvas
166
+ */
167
+ function reduceImageSize(imageCanvas, maxWidth, maxHeight = null) {
168
+
169
+ let newWidth = imageCanvas.width;
170
+ let newHeight = imageCanvas.height;
171
+
172
+ if (maxHeight !== null) {
173
+ if (newWidth > maxWidth) {
174
+ newHeight *= maxWidth / newWidth;
175
+ newWidth = maxWidth;
176
+ }
177
+
178
+ if (newHeight > maxHeight) {
179
+ newWidth *= maxHeight / newHeight;
180
+ newHeight = maxHeight;
181
+ }
182
+ } else {
183
+ const aspectRatio = imageCanvas.width / imageCanvas.height;
184
+ if (imageCanvas.width > imageCanvas.height) {
185
+ if (imageCanvas.width > maxWidth) {
186
+ newWidth = maxWidth;
187
+ newHeight = maxWidth / aspectRatio;
188
+ }
189
+ } else if (imageCanvas.height > maxWidth) {
190
+ newHeight = maxWidth;
191
+ newWidth = maxWidth * aspectRatio;
192
+ }
193
+ }
194
+
195
+
196
+ if (newWidth === imageCanvas.width
197
+ && newHeight === imageCanvas.height) {
198
+ return imageCanvas;
199
+ }
200
+
201
+ const newCanvas = document.createElement('canvas');
202
+ const newContext = newCanvas.getContext('2d');
203
+
204
+ newCanvas.width = newWidth;
205
+ newCanvas.height = newHeight;
206
+ newContext.drawImage(imageCanvas, 0, 0, newCanvas.width, newCanvas.height);
207
+
208
+ return newCanvas;
209
+ }
210
+
211
+ var CameraUtils = /*#__PURE__*/Object.freeze({
212
+ __proto__: null,
213
+ convertFov: convertFov,
214
+ calcDisplayedFov: calcDisplayedFov,
215
+ canvasToBase64: canvasToBase64,
216
+ createCameraCalibrationFromWidthHeightFov: createCameraCalibrationFromWidthHeightFov,
217
+ convertToGrayscale: convertToGrayscale,
218
+ reduceImageSize: reduceImageSize
219
+ });
220
+
221
+ // Helped from https://github.com/jeromeetienne/AR.js/blob/master/three.js/src/threex/threex-artoolkitsource.js
222
+
223
+ // Camera preview will fill component bounds without transformations.
224
+ // If the component aspect ratio is different from camera aspect ratio,
225
+ // camera preview is extended then cropped.
226
+ class Camera extends EventEmitter {
227
+
228
+ static State = {
229
+ STOPPED: 'stopped',
230
+ STARTING: 'starting',
231
+ STARTED: 'started',
232
+ STOPPING: 'stopping'
233
+ }
234
+
235
+ static DEFAULT_OPTIONS = {
236
+ width: 1024,
237
+ height: 768,
238
+ resizeOnWindowChange: false
239
+ };
240
+
241
+ static GENERIC_HARDWARE_VERTICAL_FOV = 60;
242
+
243
+ videoContainer;
244
+ videoElement;
245
+
246
+ fov = null;
247
+
248
+ _state = Camera.State.STOPPED;
249
+
250
+ _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;
251
+ _resizeOnWindowChange;
252
+
253
+ /**
254
+ * @param {Node} container
255
+ * @param {{width: number, height: number, resizeOnWindowChange: boolean}} options
256
+ */
257
+ constructor(container, options = {}) {
258
+ super();
259
+
260
+ options = Object.assign({}, Camera.DEFAULT_OPTIONS, options);
261
+
262
+ SharedCameras$1._add(this, container);
263
+
264
+ this.videoContainer = container;
265
+
266
+ this._resizeOnWindowChange = options.resizeOnWindowChange;
267
+ this._userMediaConstraints = {
268
+ audio: false,
269
+ video: {
270
+ facingMode: 'environment',
271
+ width: { ideal: options.width },
272
+ height: { ideal: options.height },
273
+ resizeMode: 'none'
274
+ }
275
+ };
276
+
277
+ this.videoContainer.style.overflow = 'hidden';
278
+
279
+ this.videoElement = document.createElement('video');
280
+ this.videoElement.setAttribute('id', 'wemap-camera');
281
+ this.videoElement.setAttribute('preload', 'none');
282
+ this.videoElement.setAttribute('muted', '');
283
+ this.videoElement.setAttribute('playsinline', '');
284
+ this.videoContainer.appendChild(this.videoElement);
285
+ }
286
+
287
+ async start(videoMediaConstraints = null) {
288
+
289
+ if (this._state !== Camera.State.STOPPED) {
290
+ throw new Error('Camera is already started');
291
+ }
292
+
293
+ if (this._state === Camera.State.STOPPING) {
294
+ await new Promise(resolve => this.once('stopped', resolve));
295
+ }
296
+
297
+ this._state = Camera.State.STARTING;
298
+ this.emit('starting');
299
+
300
+ await Camera.checkAvailability();
301
+
302
+ if (videoMediaConstraints) {
303
+ Object.assign(this._userMediaConstraints.video,
304
+ this._userMediaConstraints.video, videoMediaConstraints);
305
+ }
306
+
307
+ const stream = await navigator.mediaDevices.getUserMedia(this._userMediaConstraints);
308
+
309
+ // Listeners have to be set before srcObject assigment or they can be never called
310
+ this.videoElement.oncanplaythrough = () => this.videoElement.play();
311
+ const metadataPr = new Promise(resolve => (this.videoElement.onloadedmetadata = resolve));
312
+
313
+ this.videoElement.srcObject = this.videoStream = stream;
314
+ await metadataPr;
315
+
316
+ this._resizeElement();
317
+ this._computeFov();
318
+
319
+ if (this._resizeOnWindowChange) {
320
+ window.addEventListener('resize', this.notifyContainerSizeChanged);
321
+ }
322
+
323
+ this._state = Camera.State.STARTED;
324
+ this.emit('started', {
325
+ videoElement: this.videoElement,
326
+ stream
327
+ });
328
+ }
329
+
330
+ async stop() {
331
+
332
+ if (this._state === Camera.State.STOPPED) {
333
+ throw new Error('Camera is already stopped');
334
+ }
335
+
336
+ if (this._state === Camera.State.STARTING) {
337
+ await new Promise(resolve => this.once('started', resolve));
338
+ }
339
+
340
+ this._state = Camera.State.STOPPING;
341
+ this.emit('stopping', this);
342
+
343
+ if (this.videoStream && this.videoStream.stop) {
344
+ // compatibility with old JS API
345
+ this.videoStream.stop();
346
+ } else if (this.videoStream) {
347
+ // new JS API
348
+ this.videoStream.getVideoTracks().forEach(track => track.stop());
349
+ }
350
+ this.videoStream = null;
351
+ this.videoElement.srcObject = null;
352
+
353
+ if (this._resizeOnWindowChange) {
354
+ window.removeEventListener('resize', this.notifyContainerSizeChanged);
355
+ }
356
+
357
+ this._state = Camera.State.STOPPED;
358
+ this.emit('stopped', this);
359
+ }
360
+
361
+ release() {
362
+ this.videoContainer.removeChild(this.videoElement);
363
+ SharedCameras$1._remove(this);
364
+ }
365
+
366
+ static async checkAvailability(testUserMedia = false) {
367
+
368
+ if (!navigator.mediaDevices) {
369
+ throw new Error('navigator.mediaDevices not present in your browser');
370
+ }
371
+
372
+ if (!navigator.mediaDevices.enumerateDevices) {
373
+ throw new Error('navigator.mediaDevices.enumerateDevices not present in your browser');
374
+ }
375
+
376
+ if (!navigator.mediaDevices.getUserMedia) {
377
+ throw new Error('navigator.mediaDevices.getUserMedia not present in your browser');
378
+ }
379
+
380
+ const devices = await navigator.mediaDevices.enumerateDevices();
381
+ if (!devices.find(device => device.kind === 'videoinput')) {
382
+ throw new Error('No camera found');
383
+ }
384
+
385
+ if (testUserMedia) {
386
+ const stream = await navigator.mediaDevices.getUserMedia({
387
+ audio: false, video: { facingMode: 'environment' }
388
+ });
389
+ if (stream.stop) {
390
+ stream.stop();
391
+ } else {
392
+ stream.getVideoTracks().forEach(track => track.stop());
393
+ }
394
+ }
395
+ }
396
+
397
+ get state() {
398
+ return this._state;
399
+ }
400
+
401
+ get hardwareVerticalFov() {
402
+ return this._hardwareVerticalFov;
403
+ }
404
+
405
+ set hardwareVerticalFov(hardwareVerticalFov) {
406
+ this._hardwareVerticalFov = hardwareVerticalFov;
407
+ this._computeFov();
408
+ }
409
+
410
+ notifyContainerSizeChanged = () => {
411
+ this._resizeElement();
412
+ this._computeFov();
413
+ }
414
+
415
+ _resizeElement = () => {
416
+
417
+ if (!this.videoElement) {
418
+ throw new Error('No video element found');
419
+ }
420
+
421
+ const sourceWidth = this.videoElement.videoWidth;
422
+ const sourceHeight = this.videoElement.videoHeight;
423
+
424
+ const containerWidth = this.videoContainer.offsetWidth;
425
+ const containerHeight = this.videoContainer.offsetHeight;
426
+
427
+ const sourceAspect = sourceWidth / sourceHeight;
428
+ const screenAspect = containerWidth / containerHeight;
429
+
430
+ if (screenAspect < sourceAspect) {
431
+
432
+ const newWidth = sourceAspect * containerHeight;
433
+ this.videoElement.style.width = newWidth + 'px';
434
+ this.videoElement.style.marginLeft = -(newWidth - containerWidth) / 2 + 'px';
435
+
436
+ this.videoElement.style.height = containerHeight + 'px';
437
+ this.videoElement.style.marginTop = '0px';
438
+
439
+ } else {
440
+
441
+ const newHeight = 1 / (sourceAspect / containerWidth);
442
+ this.videoElement.style.height = newHeight + 'px';
443
+ this.videoElement.style.marginTop = -(newHeight - containerHeight) / 2 + 'px';
444
+
445
+ this.videoElement.style.width = containerWidth + 'px';
446
+ this.videoElement.style.marginLeft = '0px';
447
+ }
448
+
449
+ }
450
+
451
+ _computeFov = () => {
452
+
453
+ this.fov = calcDisplayedFov(
454
+ this.videoContainer,
455
+ this.videoElement,
456
+ this._hardwareVerticalFov
457
+ );
458
+
459
+ this.emit('fov.changed', this.fov);
460
+ }
461
+
462
+ /**
463
+ * @returns {Promise<HTMLCanvasElement>}
464
+ */
465
+ get currentImage() {
466
+
467
+ if (this._state !== Camera.State.STARTED) {
468
+ Logger.warn('Trying to access image but video is not started');
469
+ return null;
470
+ }
471
+
472
+ const { videoWidth: width, videoHeight: height } = this.videoElement;
473
+ const canvas = document.createElement('canvas');
474
+ const context = canvas.getContext('2d');
475
+ // context.filter = 'grayscale(100%)';
476
+
477
+ canvas.width = width;
478
+ canvas.height = height;
479
+
480
+ context.drawImage(this.videoElement, 0, 0, width, height);
481
+ return Promise.resolve(canvas);
482
+ }
483
+ }
484
+
485
+ /* eslint-disable max-statements */
486
+
487
+ class QrCodeScanner extends EventEmitter {
488
+
489
+ static DEFAULT_FORMATS = [
490
+ BarcodeFormat.QR_CODE,
491
+ BarcodeFormat.DATA_MATRIX,
492
+ BarcodeFormat.CODABAR
493
+ ];
494
+
495
+ /**
496
+ * @type {Camera}
497
+ */
498
+ _camera;
499
+
500
+ /**
501
+ * @type {HTMLVideoElement}
502
+ */
503
+ _videoElement;
504
+
505
+ /**
506
+ * @type {HTMLCanvasElement}
507
+ */
508
+ _canvas;
509
+
510
+ /**
511
+ * @type {CanvasRenderingContext2D}
512
+ */
513
+ _ctx;
514
+
515
+ /**
516
+ * @type {MultiFormatReader}
517
+ */
518
+ _codeReader;
519
+
520
+ /**
521
+ * @type {number}
522
+ */
523
+ _intervalTime;
524
+
525
+ /**
526
+ * @type {number}
527
+ */
528
+ _intervalTick;
529
+
530
+ /**
531
+ * @type {boolean}
532
+ */
533
+ _isStarted = false;
534
+
535
+
536
+ /**
537
+ * @param {Camera} camera
538
+ */
539
+ constructor(camera) {
540
+ super();
541
+
542
+ this._camera = camera;
543
+ this._videoElement = camera.videoElement;
544
+
545
+ this._codeReader = new MultiFormatReader();
546
+
547
+ this._canvas = document.createElement('canvas');
548
+ this._ctx = this._canvas.getContext('2d');
549
+
550
+ this._camera.on('started', () => this._tryStart());
551
+ this._camera.on('stopped', () => this._stop());
552
+ }
553
+
554
+ start(intervalTime = 500, formats = QrCodeScanner.DEFAULT_FORMATS) {
555
+ this._intervalTime = intervalTime;
556
+
557
+ if (this._isStarted) {
558
+ return;
559
+ }
560
+ this._isStarted = true;
561
+
562
+ const hints = new Map();
563
+ hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
564
+ this._codeReader.setHints(hints);
565
+
566
+ this._tryStart();
567
+ }
568
+
569
+ stop() {
570
+ this._isStarted = false;
571
+ this._stop();
572
+ }
573
+
574
+ _tryStart() {
575
+
576
+ if (!this._isStarted || this._camera.state !== Camera.State.STARTED) {
577
+ return;
578
+ }
579
+
580
+ this._width = this._videoElement.videoWidth;
581
+ this._height = this._videoElement.videoHeight;
582
+ this._canvas.width = this._width;
583
+ this._canvas.height = this._height;
584
+ this._luminances = new Uint8Array(this._width * this._height);
585
+
586
+ this._intervalTick = setInterval(() => this._onFrame(), this._intervalTime);
587
+ }
588
+
589
+ _stop() {
590
+ clearInterval(this._intervalTick);
591
+ }
592
+
593
+ _onFrame() {
594
+
595
+ if (!this._width || !this._height) {
596
+ return;
597
+ }
598
+
599
+ this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
600
+ const img = this._ctx.getImageData(0, 0, this._width, this._height);
601
+
602
+ for (let i = 0; i < this._luminances.length; i++) {
603
+ // eslint-disable-next-line no-bitwise
604
+ this._luminances[i] = ((img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4) & 0xFF;
605
+ }
606
+
607
+ const binaryBitmap = new BinaryBitmap(
608
+ new HybridBinarizer(
609
+ new RGBLuminanceSource(this._luminances, this._width, this._height)
610
+ )
611
+ );
612
+
613
+ try {
614
+ const result = this._codeReader.decodeWithState(binaryBitmap);
615
+ this.emit('scan', result.getText());
616
+ } catch (e) {
617
+ // do nothing
618
+ }
619
+ }
620
+ }
621
+
622
+ export { Camera, CameraUtils, QrCodeScanner, SharedCameras$1 as SharedCameras };
623
+ //# sourceMappingURL=wemap-camera.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wemap-camera.es.js","sources":["../src/SharedCameras.js","../src/CameraUtils.js","../src/Camera.js","../src/QrCodeScanner.js"],"sourcesContent":["import EventEmitter from 'events';\n\nimport Camera from './Camera.js';\n\nclass SharedCameras extends EventEmitter {\n\n /**\n * Singleton pattern.\n * @returns {SharedCameras}\n */\n static get instance() {\n if (!this._instance) {\n this._instance = new SharedCameras();\n }\n return this._instance;\n }\n\n /** @type {{container: Node, camera: Camera}[]} */\n _list = [];\n\n /**\n * @param {Camera} camera\n * @param {Node}\n */\n _add(camera, container) {\n const obj = {\n camera,\n container\n };\n this._list.push(obj);\n this.emit('added', obj);\n }\n\n /**\n * @param {Camera} camera\n */\n _remove(camera) {\n this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);\n this.emit('removed', { camera });\n }\n\n /**\n * @returns {{container: Node, camera: Camera}[]}\n */\n get list() {\n return this._list;\n }\n\n /**\n * @param {Node} container\n * @returns {?Camera}\n */\n getCameraByContainer(container) {\n for (const {\n camera, container: _container\n } of this._list) {\n if (container === _container) {\n return camera;\n }\n }\n return null;\n }\n}\n\nexport default SharedCameras.instance;\n","/**\n * @param {number} angle\n * @param {number} aspectRatio\n * @returns {number}\n */\nexport function convertFov(angle, aspectRatio) {\n return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;\n}\n\n/**\n * @param {HTMLDivElement} videoContainer\n * @param {HTMLVideoElement} videoElement\n * @param {number} hardwareVerticalFov\n * @returns {object}\n */\nexport function calcDisplayedFov(videoContainer, videoElement, hardwareVerticalFov) {\n\n const sourceWidth = videoElement.videoWidth;\n const sourceHeight = videoElement.videoHeight;\n\n const containerWidth = videoContainer.offsetWidth;\n const containerHeight = videoContainer.offsetHeight;\n\n const sourceAspect = sourceWidth / sourceHeight;\n const screenAspect = containerWidth / containerHeight;\n\n let fovV = hardwareVerticalFov;\n let fovH = convertFov(fovV, sourceAspect);\n\n if (screenAspect < sourceAspect) {\n fovH = convertFov(fovV, screenAspect);\n } else {\n fovV = convertFov(fovH, 1 / screenAspect);\n }\n\n return {\n vertical: fovV,\n horizontal: fovH\n };\n}\n\n/**\n * @param {HTMLCanvasElement} canvas\n * @param {string} type\n * @param {any} quality\n * @returns {string}\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n */\nexport function canvasToBase64(canvas, type = 'image/png', quality = null) {\n return canvas\n .toDataURL(type, quality)\n .substring(('data:' + type + ';base64,').length);\n}\n\n/**\n * Creates a pinhole camera from dimensions and a field of view\n * Distortions are not considered\n *\n * @param {number} width the camera width\n * @param {number} height the camera height\n * @param {number} fovOfWidth the field of view along the width axis in radians\n * @returns {object} the calibration matrix\n */\nexport function createCameraCalibrationFromWidthHeightFov(width, height, fovOfWidth) {\n\n const fovOfHeight = 2 * Math.atan(height * Math.tan(fovOfWidth / 2) / width);\n\n const fx = width / (2 * Math.tan(0.5 * fovOfWidth));\n const fy = height / (2 * Math.tan(0.5 * fovOfHeight));\n const cx = width / 2;\n const cy = height / 2;\n\n return {\n intrinsic: [fx, 0, cx, 0, fy, cy, 0, 0, 1],\n distortions: [0, 0, 0, 0, 0]\n };\n}\n\n/**\n * @param {HTMLCanvasElement} imageCanvas\n */\nexport function convertToGrayscale(imageCanvas) {\n const ctx = imageCanvas.getContext('2d');\n const imgData = ctx.getImageData(0, 0, imageCanvas.width, imageCanvas.height);\n const pixels = imgData.data;\n for (let i = 0; i < pixels.length; i += 4) {\n\n const lightness = parseInt((pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3, 10);\n pixels[i] = lightness;\n pixels[i + 1] = lightness;\n pixels[i + 2] = lightness;\n }\n ctx.putImageData(imgData, 0, 0);\n}\n\n/**\n * Reduce image size and keep aspect ratio\n * @param {HTMLCanvasElement} imageCanvas\n */\nexport function reduceImageSize(imageCanvas, maxWidth, maxHeight = null) {\n\n let newWidth = imageCanvas.width;\n let newHeight = imageCanvas.height;\n\n if (maxHeight !== null) {\n if (newWidth > maxWidth) {\n newHeight *= maxWidth / newWidth;\n newWidth = maxWidth;\n }\n\n if (newHeight > maxHeight) {\n newWidth *= maxHeight / newHeight;\n newHeight = maxHeight;\n }\n } else {\n const aspectRatio = imageCanvas.width / imageCanvas.height;\n if (imageCanvas.width > imageCanvas.height) {\n if (imageCanvas.width > maxWidth) {\n newWidth = maxWidth;\n newHeight = maxWidth / aspectRatio;\n }\n } else if (imageCanvas.height > maxWidth) {\n newHeight = maxWidth;\n newWidth = maxWidth * aspectRatio;\n }\n }\n\n\n if (newWidth === imageCanvas.width\n && newHeight === imageCanvas.height) {\n return imageCanvas;\n }\n\n const newCanvas = document.createElement('canvas');\n const newContext = newCanvas.getContext('2d');\n\n newCanvas.width = newWidth;\n newCanvas.height = newHeight;\n newContext.drawImage(imageCanvas, 0, 0, newCanvas.width, newCanvas.height);\n\n return newCanvas;\n}\n\n","import EventEmitter from 'events';\n\nimport Logger from '@wemap/logger';\nimport SharedCameras from './SharedCameras.js';\nimport { calcDisplayedFov } from './CameraUtils.js';\n\n// Helped from https://github.com/jeromeetienne/AR.js/blob/master/three.js/src/threex/threex-artoolkitsource.js\n\n// Camera preview will fill component bounds without transformations.\n// If the component aspect ratio is different from camera aspect ratio,\n// camera preview is extended then cropped.\nclass Camera extends EventEmitter {\n\n static State = {\n STOPPED: 'stopped',\n STARTING: 'starting',\n STARTED: 'started',\n STOPPING: 'stopping'\n }\n\n static DEFAULT_OPTIONS = {\n width: 1024,\n height: 768,\n resizeOnWindowChange: false\n };\n\n static GENERIC_HARDWARE_VERTICAL_FOV = 60;\n\n videoContainer;\n videoElement;\n\n fov = null;\n\n _state = Camera.State.STOPPED;\n\n _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;\n _resizeOnWindowChange;\n\n /**\n * @param {Node} container\n * @param {{width: number, height: number, resizeOnWindowChange: boolean}} options\n */\n constructor(container, options = {}) {\n super();\n\n options = Object.assign({}, Camera.DEFAULT_OPTIONS, options);\n\n SharedCameras._add(this, container);\n\n this.videoContainer = container;\n\n this._resizeOnWindowChange = options.resizeOnWindowChange;\n this._userMediaConstraints = {\n audio: false,\n video: {\n facingMode: 'environment',\n width: { ideal: options.width },\n height: { ideal: options.height },\n resizeMode: 'none'\n }\n };\n\n this.videoContainer.style.overflow = 'hidden';\n\n this.videoElement = document.createElement('video');\n this.videoElement.setAttribute('id', 'wemap-camera');\n this.videoElement.setAttribute('preload', 'none');\n this.videoElement.setAttribute('muted', '');\n this.videoElement.setAttribute('playsinline', '');\n this.videoContainer.appendChild(this.videoElement);\n }\n\n async start(videoMediaConstraints = null) {\n\n if (this._state !== Camera.State.STOPPED) {\n throw new Error('Camera is already started');\n }\n\n if (this._state === Camera.State.STOPPING) {\n await new Promise(resolve => this.once('stopped', resolve));\n }\n\n this._state = Camera.State.STARTING;\n this.emit('starting');\n\n await Camera.checkAvailability();\n\n if (videoMediaConstraints) {\n Object.assign(this._userMediaConstraints.video,\n this._userMediaConstraints.video, videoMediaConstraints);\n }\n\n const stream = await navigator.mediaDevices.getUserMedia(this._userMediaConstraints);\n\n // Listeners have to be set before srcObject assigment or they can be never called\n this.videoElement.oncanplaythrough = () => this.videoElement.play();\n const metadataPr = new Promise(resolve => (this.videoElement.onloadedmetadata = resolve));\n\n this.videoElement.srcObject = this.videoStream = stream;\n await metadataPr;\n\n this._resizeElement();\n this._computeFov();\n\n if (this._resizeOnWindowChange) {\n window.addEventListener('resize', this.notifyContainerSizeChanged);\n }\n\n this._state = Camera.State.STARTED;\n this.emit('started', {\n videoElement: this.videoElement,\n stream\n });\n }\n\n async stop() {\n\n if (this._state === Camera.State.STOPPED) {\n throw new Error('Camera is already stopped');\n }\n\n if (this._state === Camera.State.STARTING) {\n await new Promise(resolve => this.once('started', resolve));\n }\n\n this._state = Camera.State.STOPPING;\n this.emit('stopping', this);\n\n if (this.videoStream && this.videoStream.stop) {\n // compatibility with old JS API\n this.videoStream.stop();\n } else if (this.videoStream) {\n // new JS API\n this.videoStream.getVideoTracks().forEach(track => track.stop());\n }\n this.videoStream = null;\n this.videoElement.srcObject = null;\n\n if (this._resizeOnWindowChange) {\n window.removeEventListener('resize', this.notifyContainerSizeChanged);\n }\n\n this._state = Camera.State.STOPPED;\n this.emit('stopped', this);\n }\n\n release() {\n this.videoContainer.removeChild(this.videoElement);\n SharedCameras._remove(this);\n }\n\n static async checkAvailability(testUserMedia = false) {\n\n if (!navigator.mediaDevices) {\n throw new Error('navigator.mediaDevices not present in your browser');\n }\n\n if (!navigator.mediaDevices.enumerateDevices) {\n throw new Error('navigator.mediaDevices.enumerateDevices not present in your browser');\n }\n\n if (!navigator.mediaDevices.getUserMedia) {\n throw new Error('navigator.mediaDevices.getUserMedia not present in your browser');\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices();\n if (!devices.find(device => device.kind === 'videoinput')) {\n throw new Error('No camera found');\n }\n\n if (testUserMedia) {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: false, video: { facingMode: 'environment' }\n });\n if (stream.stop) {\n stream.stop();\n } else {\n stream.getVideoTracks().forEach(track => track.stop());\n }\n }\n }\n\n get state() {\n return this._state;\n }\n\n get hardwareVerticalFov() {\n return this._hardwareVerticalFov;\n }\n\n set hardwareVerticalFov(hardwareVerticalFov) {\n this._hardwareVerticalFov = hardwareVerticalFov;\n this._computeFov();\n }\n\n notifyContainerSizeChanged = () => {\n this._resizeElement();\n this._computeFov();\n }\n\n _resizeElement = () => {\n\n if (!this.videoElement) {\n throw new Error('No video element found');\n }\n\n const sourceWidth = this.videoElement.videoWidth;\n const sourceHeight = this.videoElement.videoHeight;\n\n const containerWidth = this.videoContainer.offsetWidth;\n const containerHeight = this.videoContainer.offsetHeight;\n\n const sourceAspect = sourceWidth / sourceHeight;\n const screenAspect = containerWidth / containerHeight;\n\n if (screenAspect < sourceAspect) {\n\n const newWidth = sourceAspect * containerHeight;\n this.videoElement.style.width = newWidth + 'px';\n this.videoElement.style.marginLeft = -(newWidth - containerWidth) / 2 + 'px';\n\n this.videoElement.style.height = containerHeight + 'px';\n this.videoElement.style.marginTop = '0px';\n\n } else {\n\n const newHeight = 1 / (sourceAspect / containerWidth);\n this.videoElement.style.height = newHeight + 'px';\n this.videoElement.style.marginTop = -(newHeight - containerHeight) / 2 + 'px';\n\n this.videoElement.style.width = containerWidth + 'px';\n this.videoElement.style.marginLeft = '0px';\n }\n\n }\n\n _computeFov = () => {\n\n this.fov = calcDisplayedFov(\n this.videoContainer,\n this.videoElement,\n this._hardwareVerticalFov\n );\n\n this.emit('fov.changed', this.fov);\n }\n\n /**\n * @returns {Promise<HTMLCanvasElement>}\n */\n get currentImage() {\n\n if (this._state !== Camera.State.STARTED) {\n Logger.warn('Trying to access image but video is not started');\n return null;\n }\n\n const { videoWidth: width, videoHeight: height } = this.videoElement;\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n // context.filter = 'grayscale(100%)';\n\n canvas.width = width;\n canvas.height = height;\n\n context.drawImage(this.videoElement, 0, 0, width, height);\n return Promise.resolve(canvas);\n }\n}\n\nexport default Camera;\n","/* eslint-disable max-statements */\nimport EventEmitter from 'events';\nimport {\n MultiFormatReader,\n BarcodeFormat,\n DecodeHintType,\n RGBLuminanceSource,\n BinaryBitmap,\n HybridBinarizer\n} from '@zxing/library';\n\nimport Camera from './Camera.js';\n\nclass QrCodeScanner extends EventEmitter {\n\n static DEFAULT_FORMATS = [\n BarcodeFormat.QR_CODE,\n BarcodeFormat.DATA_MATRIX,\n BarcodeFormat.CODABAR\n ];\n\n /**\n * @type {Camera}\n */\n _camera;\n\n /**\n * @type {HTMLVideoElement}\n */\n _videoElement;\n\n /**\n * @type {HTMLCanvasElement}\n */\n _canvas;\n\n /**\n * @type {CanvasRenderingContext2D}\n */\n _ctx;\n\n /**\n * @type {MultiFormatReader}\n */\n _codeReader;\n\n /**\n * @type {number}\n */\n _intervalTime;\n\n /**\n * @type {number}\n */\n _intervalTick;\n\n /**\n * @type {boolean}\n */\n _isStarted = false;\n\n\n /**\n * @param {Camera} camera\n */\n constructor(camera) {\n super();\n\n this._camera = camera;\n this._videoElement = camera.videoElement;\n\n this._codeReader = new MultiFormatReader();\n\n this._canvas = document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d');\n\n this._camera.on('started', () => this._tryStart());\n this._camera.on('stopped', () => this._stop());\n }\n\n start(intervalTime = 500, formats = QrCodeScanner.DEFAULT_FORMATS) {\n this._intervalTime = intervalTime;\n\n if (this._isStarted) {\n return;\n }\n this._isStarted = true;\n\n const hints = new Map();\n hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);\n this._codeReader.setHints(hints);\n\n this._tryStart();\n }\n\n stop() {\n this._isStarted = false;\n this._stop();\n }\n\n _tryStart() {\n\n if (!this._isStarted || this._camera.state !== Camera.State.STARTED) {\n return;\n }\n\n this._width = this._videoElement.videoWidth;\n this._height = this._videoElement.videoHeight;\n this._canvas.width = this._width;\n this._canvas.height = this._height;\n this._luminances = new Uint8Array(this._width * this._height);\n\n this._intervalTick = setInterval(() => this._onFrame(), this._intervalTime);\n }\n\n _stop() {\n clearInterval(this._intervalTick);\n }\n\n _onFrame() {\n\n if (!this._width || !this._height) {\n return;\n }\n\n this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);\n const img = this._ctx.getImageData(0, 0, this._width, this._height);\n\n for (let i = 0; i < this._luminances.length; i++) {\n // eslint-disable-next-line no-bitwise\n this._luminances[i] = ((img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4) & 0xFF;\n }\n\n const binaryBitmap = new BinaryBitmap(\n new HybridBinarizer(\n new RGBLuminanceSource(this._luminances, this._width, this._height)\n )\n );\n\n try {\n const result = this._codeReader.decodeWithState(binaryBitmap);\n this.emit('scan', result.getText());\n } catch (e) {\n // do nothing\n }\n }\n}\n\nexport default QrCodeScanner;\n"],"names":["SharedCameras"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -19,12 +19,14 @@ declare module '@wemap/camera' {
19
19
  ): { vertical: number, horizontal: number };
20
20
  static createCameraCalibrationFromWidthHeightFov(width: number, height: number, fovOfWidth: number): Calibration;
21
21
  static canvasToBase64(canvas: HTMLCanvasElement, type?: string, quality?: any): string;
22
+ static base64ToCanvas(base64: string, type?: string): HTMLCanvasElement;
22
23
  static convertToGrayscale(imageCanvas: HTMLCanvasElement): void;
23
24
  static reduceImageSize(
24
25
  imageCanvas: HTMLCanvasElement,
25
26
  maxWidth: number,
26
27
  maxHeight?: number
27
28
  ): HTMLCanvasElement;
29
+ static htmlImageToCanvas(image: HTMLImageElement): HTMLCanvasElement;
28
30
  }
29
31
 
30
32
  export type CameraOptions = {
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/camera"
13
13
  },
14
14
  "name": "@wemap/camera",
15
- "version": "9.2.0",
15
+ "version": "10.0.0-alpha.10",
16
16
  "bugs": {
17
17
  "url": "https://github.com/wemap/wemap-modules-js/issues"
18
18
  },
@@ -24,7 +24,7 @@
24
24
  ],
25
25
  "license": "ISC",
26
26
  "dependencies": {
27
- "@wemap/logger": "^9.2.0",
27
+ "@wemap/logger": "^10.0.0-alpha.8",
28
28
  "@zxing/library": "^0.18.4",
29
29
  "events": "^3.3.0"
30
30
  },
@@ -32,5 +32,5 @@
32
32
  "@types/events": "^3.0.0"
33
33
  },
34
34
  "type": "module",
35
- "gitHead": "9eee407fadd76fc5cbfbeeb13be0d74391ca5d9e"
35
+ "gitHead": "5970289ef2b1f06f9df382878f72bf48f57c4c00"
36
36
  }
package/src/Camera.js CHANGED
@@ -239,6 +239,10 @@ class Camera extends EventEmitter {
239
239
 
240
240
  _computeFov = () => {
241
241
 
242
+ if ([Camera.State.STOPPING, Camera.State.STOPPED].includes(this.state)) {
243
+ return;
244
+ }
245
+
242
246
  this.fov = calcDisplayedFov(
243
247
  this.videoContainer,
244
248
  this.videoElement,
@@ -11,13 +11,17 @@ export function convertFov(angle, aspectRatio) {
11
11
  * @param {HTMLDivElement} videoContainer
12
12
  * @param {HTMLVideoElement} videoElement
13
13
  * @param {number} hardwareVerticalFov
14
- * @returns {object}
14
+ * @returns {object|null}
15
15
  */
16
16
  export function calcDisplayedFov(videoContainer, videoElement, hardwareVerticalFov) {
17
17
 
18
18
  const sourceWidth = videoElement.videoWidth;
19
19
  const sourceHeight = videoElement.videoHeight;
20
20
 
21
+ if (!sourceWidth || !sourceHeight) {
22
+ return null;
23
+ }
24
+
21
25
  const containerWidth = videoContainer.offsetWidth;
22
26
  const containerHeight = videoContainer.offsetHeight;
23
27
 
@@ -52,6 +56,23 @@ export function canvasToBase64(canvas, type = 'image/png', quality = null) {
52
56
  .substring(('data:' + type + ';base64,').length);
53
57
  }
54
58
 
59
+ /**
60
+ * @param {string} base64
61
+ * @returns {HTMLCanvasElement}
62
+ */
63
+ export function base64ToCanvas(base64, type = 'image/png') {
64
+ if (type !== 'image/png') {
65
+ throw new Error('Only image/png is supported');
66
+ }
67
+ const canvas = document.createElement('canvas');
68
+ const image = new Image();
69
+ image.src = 'data:image/png;base64,' + base64;
70
+ canvas.width = image.width;
71
+ canvas.height = image.height;
72
+ canvas.getContext('2d').drawImage(image, 0, 0);
73
+ return canvas;
74
+ }
75
+
55
76
  /**
56
77
  * Creates a pinhole camera from dimensions and a field of view
57
78
  * Distortions are not considered
@@ -141,3 +162,16 @@ export function reduceImageSize(imageCanvas, maxWidth, maxHeight = null) {
141
162
  return newCanvas;
142
163
  }
143
164
 
165
+ /**
166
+ * @param {HTMLImageElement} image
167
+ * @returns {HTMLCanvasElement}
168
+ */
169
+ export function htmlImageToCanvas(image) {
170
+ const canvas = document.createElement('canvas');
171
+ canvas.width = image.width;
172
+ canvas.height = image.height;
173
+ const context = canvas.getContext('2d');
174
+ context.scale(canvas.width / image.width, canvas.height / image.height);
175
+ context.drawImage(image, 0, 0);
176
+ return canvas;
177
+ }