@wemap/camera 12.0.0 → 12.10.6
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.
- package/dist/index.js +12 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -34
- package/dist/index.mjs.map +1 -1
- package/index.ts +0 -1
- package/package.json +6 -5
- package/src/QrCodeScanner.ts +10 -42
package/dist/index.js
CHANGED
|
@@ -6,9 +6,9 @@ var __publicField = (obj, key, value) => {
|
|
|
6
6
|
return value;
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
|
-
const library = require("@zxing/library");
|
|
10
9
|
const EventEmitter = require("events");
|
|
11
10
|
const Logger = require("@wemap/logger");
|
|
11
|
+
const QrScanner = require("qr-scanner");
|
|
12
12
|
class SharedCameras extends EventEmitter {
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
@@ -328,37 +328,31 @@ __publicField(_Camera, "DEFAULT_OPTIONS", {
|
|
|
328
328
|
});
|
|
329
329
|
__publicField(_Camera, "GENERIC_HARDWARE_VERTICAL_FOV", 60);
|
|
330
330
|
let Camera = _Camera;
|
|
331
|
-
|
|
331
|
+
class QrCodeScanner extends EventEmitter {
|
|
332
332
|
constructor(camera) {
|
|
333
333
|
super();
|
|
334
334
|
__publicField(this, "_height");
|
|
335
|
-
__publicField(this, "_luminances");
|
|
336
335
|
__publicField(this, "_width");
|
|
337
336
|
__publicField(this, "_camera");
|
|
338
337
|
__publicField(this, "_videoElement");
|
|
339
338
|
__publicField(this, "_canvas");
|
|
340
339
|
__publicField(this, "_ctx");
|
|
341
|
-
__publicField(this, "_codeReader");
|
|
342
340
|
__publicField(this, "_intervalTime", null);
|
|
343
341
|
__publicField(this, "_intervalTick");
|
|
344
342
|
__publicField(this, "_isStarted", false);
|
|
345
343
|
this._camera = camera;
|
|
346
344
|
this._videoElement = camera.videoElement;
|
|
347
|
-
this._codeReader = new library.MultiFormatReader();
|
|
348
345
|
this._canvas = document.createElement("canvas");
|
|
349
346
|
this._ctx = this._canvas.getContext("2d");
|
|
350
347
|
this._camera.on("started", () => this._tryStart());
|
|
351
348
|
this._camera.on("stopped", () => this._stop());
|
|
352
349
|
}
|
|
353
|
-
start(intervalTime = 500
|
|
350
|
+
start(intervalTime = 500) {
|
|
354
351
|
this._intervalTime = intervalTime;
|
|
355
352
|
if (this._isStarted) {
|
|
356
353
|
return;
|
|
357
354
|
}
|
|
358
355
|
this._isStarted = true;
|
|
359
|
-
const hints = /* @__PURE__ */ new Map();
|
|
360
|
-
hints.set(library.DecodeHintType.POSSIBLE_FORMATS, formats);
|
|
361
|
-
this._codeReader.setHints(hints);
|
|
362
356
|
this._tryStart();
|
|
363
357
|
}
|
|
364
358
|
stop() {
|
|
@@ -373,43 +367,25 @@ const _QrCodeScanner = class _QrCodeScanner extends EventEmitter {
|
|
|
373
367
|
this._height = this._videoElement.videoHeight;
|
|
374
368
|
this._canvas.width = this._width;
|
|
375
369
|
this._canvas.height = this._height;
|
|
376
|
-
this._luminances = new Uint8ClampedArray(this._width * this._height);
|
|
377
370
|
this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime);
|
|
378
371
|
}
|
|
379
372
|
_stop() {
|
|
380
373
|
clearInterval(this._intervalTick);
|
|
381
374
|
}
|
|
382
375
|
_onFrame() {
|
|
383
|
-
if (!this._width || !this._height
|
|
376
|
+
if (!this._width || !this._height) {
|
|
384
377
|
return;
|
|
385
378
|
}
|
|
386
379
|
this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
)
|
|
395
|
-
);
|
|
396
|
-
try {
|
|
397
|
-
const result = this._codeReader.decodeWithState(binaryBitmap);
|
|
398
|
-
this.emit("scan", result.getText());
|
|
399
|
-
} catch (e) {
|
|
400
|
-
}
|
|
380
|
+
const scanResultPromise = QrScanner.scanImage(this._canvas, {
|
|
381
|
+
returnDetailedScanResult: true
|
|
382
|
+
});
|
|
383
|
+
scanResultPromise.then((result) => {
|
|
384
|
+
this.emit("scan", result.data);
|
|
385
|
+
}).catch((e) => {
|
|
386
|
+
});
|
|
401
387
|
}
|
|
402
|
-
}
|
|
403
|
-
__publicField(_QrCodeScanner, "DEFAULT_FORMATS", [
|
|
404
|
-
library.BarcodeFormat.QR_CODE,
|
|
405
|
-
library.BarcodeFormat.DATA_MATRIX,
|
|
406
|
-
library.BarcodeFormat.CODABAR
|
|
407
|
-
]);
|
|
408
|
-
let QrCodeScanner = _QrCodeScanner;
|
|
409
|
-
Object.defineProperty(exports, "BarcodeFormat", {
|
|
410
|
-
enumerable: true,
|
|
411
|
-
get: () => library.BarcodeFormat
|
|
412
|
-
});
|
|
388
|
+
}
|
|
413
389
|
exports.Camera = Camera;
|
|
414
390
|
exports.QrCodeScanner = QrCodeScanner;
|
|
415
391
|
exports.SharedCameras = SharedCameras$1;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/SharedCameras.ts","../src/CameraUtils.ts","../src/Camera.ts","../src/QrCodeScanner.ts"],"sourcesContent":["import EventEmitter from 'events';\n\nimport Camera from './Camera.js';\n\ndeclare interface SharedCameras {\n on(event: 'added', listener: (obj: CameraContainer) => void): this;\n on(event: 'removed', listener: (obj: { camera: Camera }) => void): this;\n}\n\ntype CameraContainer = { container: HTMLElement, camera: Camera };\n\nclass SharedCameras extends EventEmitter {\n\n _list: CameraContainer[] = [];\n\n _add(camera: Camera, container: HTMLElement) {\n const obj = {\n camera,\n container\n };\n this._list.push(obj);\n this.emit('added', obj);\n }\n\n _remove(camera: Camera) {\n this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);\n this.emit('removed', { camera });\n }\n\n get list() {\n return this._list;\n }\n\n getCameraByContainer(container: HTMLElement) {\n return this._list.find(obj => obj.container === container)?.camera || null;\n }\n}\n\nexport default new SharedCameras();\n","export type IntrinsicParams = [number, number, number, number, number, number, number, number, number];\nexport type DistortionsParams = [number, number, number, number, number];\n\nexport type Calibration = {\n intrinsic: IntrinsicParams,\n distortions?: DistortionsParams\n}\n\nexport function convertFov(angle: number, aspectRatio: number) {\n return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;\n}\n\nexport function calcDisplayedFov(\n videoContainer: HTMLElement,\n videoElement: HTMLVideoElement,\n hardwareVerticalFov: number\n) {\n\n const sourceWidth = videoElement.videoWidth;\n const sourceHeight = videoElement.videoHeight;\n\n if (!sourceWidth || !sourceHeight) {\n return null;\n }\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 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n */\nexport function canvasToBase64(canvas: HTMLCanvasElement, type = 'image/png', quality: any = null) {\n return canvas\n .toDataURL(type, quality)\n .substring(('data:' + type + ';base64,').length);\n}\n\nexport function base64ToCanvas(base64: string, type = 'image/png') {\n if (type !== 'image/png') {\n throw new Error('Only image/png is supported');\n }\n const canvas = document.createElement('canvas');\n const image = new Image();\n image.src = 'data:image/png;base64,' + base64;\n canvas.width = image.width;\n canvas.height = image.height;\n canvas.getContext('2d')?.drawImage(image, 0, 0);\n return canvas;\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(\n width: number,\n height: number,\n fovOfWidth: number\n): Calibration {\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: HTMLCanvasElement) {\n const ctx = imageCanvas.getContext('2d');\n if (!ctx) return;\n\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 = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;\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 */\nexport function reduceImageSize(\n imageCanvas: HTMLCanvasElement,\n maxWidth: number,\n maxHeight?: number\n) {\n\n let newWidth = imageCanvas.width;\n let newHeight = imageCanvas.height;\n\n if (typeof maxHeight !== 'undefined') {\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\nexport function htmlImageToCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n canvas.width = image.width;\n canvas.height = image.height;\n const context = canvas.getContext('2d');\n context?.scale(canvas.width / image.width, canvas.height / image.height);\n context?.drawImage(image, 0, 0);\n return canvas;\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.\n\ntype CameraOptions = {\n width?: number,\n height?: number,\n resizeOnWindowChange?: boolean\n};\n\nexport type CameraState = 'stopped' | 'starting' | 'started' | 'stopping';\n\ndeclare interface Camera {\n on(event: 'starting', listener: () => void): this;\n on(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n on(event: 'stopping', listener: () => void): this;\n on(event: 'stopped', listener: () => void): this;\n on(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n off(event: 'starting', listener: () => void): this;\n off(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n off(event: 'stopping', listener: () => void): this;\n off(event: 'stopped', listener: () => void): this;\n off(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n}\n\n\nclass Camera extends EventEmitter {\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 _userMediaConstraints: MediaStreamConstraints;\n videoStream: MediaStream | null = null;\n\n videoContainer: HTMLElement;\n videoElement: HTMLVideoElement;\n\n fov: {horizontal: number, vertical: number} | null = null;\n\n _state: CameraState = 'stopped';\n\n _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;\n _resizeOnWindowChange;\n\n constructor(container: HTMLElement, options: CameraOptions = {}) {\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 } as any // For resizeMode\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?: MediaTrackConstraints) {\n\n if (this._state === 'started' || this._state === 'starting') {\n throw new Error('Camera is already started');\n }\n\n if (this._state === 'stopping') {\n await new Promise(resolve => this.once('stopped', resolve));\n }\n\n this._state = 'starting';\n this.emit('starting');\n\n await Camera.checkAvailability();\n\n if (typeof videoMediaConstraints === 'object') {\n Object.assign(this._userMediaConstraints.video as MediaTrackConstraints,\n this._userMediaConstraints.video as MediaTrackConstraints, 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 = 'started';\n const output = {\n videoElement: this.videoElement,\n stream\n };\n\n this.emit('started', output);\n return output;\n }\n\n async stop() {\n\n if (this._state === 'stopped') {\n throw new Error('Camera is already stopped');\n }\n\n if (this._state === 'starting') {\n await new Promise(resolve => this.once('started', resolve));\n }\n\n this._state = 'stopping';\n this.emit('stopping');\n\n if (this.videoStream) {\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 = 'stopped';\n this.emit('stopped');\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 stream.getVideoTracks().forEach(track => track.stop());\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 if (['stopping', 'stopped'].includes(this.state)) {\n return;\n }\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 get currentImage() {\n\n if (this._state !== 'started') {\n Logger.warn('Trying to access image but video is not started');\n return Promise.resolve(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","import 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\ndeclare interface QrCodeScanner {\n on(event: 'scan', listener: (result: string) => void): this;\n off(event: 'scan', listener: (result: string) => void): this;\n}\n\nclass QrCodeScanner extends EventEmitter {\n\n static DEFAULT_FORMATS: BarcodeFormat[] = [\n BarcodeFormat.QR_CODE,\n BarcodeFormat.DATA_MATRIX,\n BarcodeFormat.CODABAR\n ];\n\n _height?: number;\n _luminances?: Uint8ClampedArray;\n _width?: number;\n _camera: Camera;\n _videoElement: HTMLVideoElement;\n _canvas: HTMLCanvasElement;\n _ctx: CanvasRenderingContext2D;\n _codeReader: MultiFormatReader;\n _intervalTime: number | null = null;\n _intervalTick?: number;\n _isStarted = false;\n\n\n constructor(camera: 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') as CanvasRenderingContext2D;\n\n this._camera.on('started', () => this._tryStart());\n this._camera.on('stopped', () => this._stop());\n }\n\n start(intervalTime = 500, formats: BarcodeFormat[] = 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 !== '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 Uint8ClampedArray(this._width * this._height);\n\n this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime as number);\n }\n\n _stop() {\n clearInterval(this._intervalTick);\n }\n\n _onFrame() {\n\n if (!this._width || !this._height || !this._luminances) {\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","MultiFormatReader","DecodeHintType","BinaryBitmap","HybridBinarizer","RGBLuminanceSource","BarcodeFormat"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/SharedCameras.ts","../src/CameraUtils.ts","../src/Camera.ts","../src/QrCodeScanner.ts"],"sourcesContent":["import EventEmitter from 'events';\n\nimport Camera from './Camera.js';\n\ndeclare interface SharedCameras {\n on(event: 'added', listener: (obj: CameraContainer) => void): this;\n on(event: 'removed', listener: (obj: { camera: Camera }) => void): this;\n}\n\ntype CameraContainer = { container: HTMLElement, camera: Camera };\n\nclass SharedCameras extends EventEmitter {\n\n _list: CameraContainer[] = [];\n\n _add(camera: Camera, container: HTMLElement) {\n const obj = {\n camera,\n container\n };\n this._list.push(obj);\n this.emit('added', obj);\n }\n\n _remove(camera: Camera) {\n this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);\n this.emit('removed', { camera });\n }\n\n get list() {\n return this._list;\n }\n\n getCameraByContainer(container: HTMLElement) {\n return this._list.find(obj => obj.container === container)?.camera || null;\n }\n}\n\nexport default new SharedCameras();\n","export type IntrinsicParams = [number, number, number, number, number, number, number, number, number];\nexport type DistortionsParams = [number, number, number, number, number];\n\nexport type Calibration = {\n intrinsic: IntrinsicParams,\n distortions?: DistortionsParams\n}\n\nexport function convertFov(angle: number, aspectRatio: number) {\n return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;\n}\n\nexport function calcDisplayedFov(\n videoContainer: HTMLElement,\n videoElement: HTMLVideoElement,\n hardwareVerticalFov: number\n) {\n\n const sourceWidth = videoElement.videoWidth;\n const sourceHeight = videoElement.videoHeight;\n\n if (!sourceWidth || !sourceHeight) {\n return null;\n }\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 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n */\nexport function canvasToBase64(canvas: HTMLCanvasElement, type = 'image/png', quality: any = null) {\n return canvas\n .toDataURL(type, quality)\n .substring(('data:' + type + ';base64,').length);\n}\n\nexport function base64ToCanvas(base64: string, type = 'image/png') {\n if (type !== 'image/png') {\n throw new Error('Only image/png is supported');\n }\n const canvas = document.createElement('canvas');\n const image = new Image();\n image.src = 'data:image/png;base64,' + base64;\n canvas.width = image.width;\n canvas.height = image.height;\n canvas.getContext('2d')?.drawImage(image, 0, 0);\n return canvas;\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(\n width: number,\n height: number,\n fovOfWidth: number\n): Calibration {\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: HTMLCanvasElement) {\n const ctx = imageCanvas.getContext('2d');\n if (!ctx) return;\n\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 = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;\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 */\nexport function reduceImageSize(\n imageCanvas: HTMLCanvasElement,\n maxWidth: number,\n maxHeight?: number\n) {\n\n let newWidth = imageCanvas.width;\n let newHeight = imageCanvas.height;\n\n if (typeof maxHeight !== 'undefined') {\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\nexport function htmlImageToCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n canvas.width = image.width;\n canvas.height = image.height;\n const context = canvas.getContext('2d');\n context?.scale(canvas.width / image.width, canvas.height / image.height);\n context?.drawImage(image, 0, 0);\n return canvas;\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.\n\ntype CameraOptions = {\n width?: number,\n height?: number,\n resizeOnWindowChange?: boolean\n};\n\nexport type CameraState = 'stopped' | 'starting' | 'started' | 'stopping';\n\ndeclare interface Camera {\n on(event: 'starting', listener: () => void): this;\n on(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n on(event: 'stopping', listener: () => void): this;\n on(event: 'stopped', listener: () => void): this;\n on(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n off(event: 'starting', listener: () => void): this;\n off(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n off(event: 'stopping', listener: () => void): this;\n off(event: 'stopped', listener: () => void): this;\n off(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n}\n\n\nclass Camera extends EventEmitter {\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 _userMediaConstraints: MediaStreamConstraints;\n videoStream: MediaStream | null = null;\n\n videoContainer: HTMLElement;\n videoElement: HTMLVideoElement;\n\n fov: {horizontal: number, vertical: number} | null = null;\n\n _state: CameraState = 'stopped';\n\n _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;\n _resizeOnWindowChange;\n\n constructor(container: HTMLElement, options: CameraOptions = {}) {\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 } as any // For resizeMode\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?: MediaTrackConstraints) {\n\n if (this._state === 'started' || this._state === 'starting') {\n throw new Error('Camera is already started');\n }\n\n if (this._state === 'stopping') {\n await new Promise(resolve => this.once('stopped', resolve));\n }\n\n this._state = 'starting';\n this.emit('starting');\n\n await Camera.checkAvailability();\n\n if (typeof videoMediaConstraints === 'object') {\n Object.assign(this._userMediaConstraints.video as MediaTrackConstraints,\n this._userMediaConstraints.video as MediaTrackConstraints, 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 = 'started';\n const output = {\n videoElement: this.videoElement,\n stream\n };\n\n this.emit('started', output);\n return output;\n }\n\n async stop() {\n\n if (this._state === 'stopped') {\n throw new Error('Camera is already stopped');\n }\n\n if (this._state === 'starting') {\n await new Promise(resolve => this.once('started', resolve));\n }\n\n this._state = 'stopping';\n this.emit('stopping');\n\n if (this.videoStream) {\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 = 'stopped';\n this.emit('stopped');\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 stream.getVideoTracks().forEach(track => track.stop());\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 if (['stopping', 'stopped'].includes(this.state)) {\n return;\n }\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 get currentImage() {\n\n if (this._state !== 'started') {\n Logger.warn('Trying to access image but video is not started');\n return Promise.resolve(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","import EventEmitter from 'events';\nimport QrScanner from 'qr-scanner';\n\nimport Camera from './Camera.js';\n\ndeclare interface QrCodeScanner {\n on(event: 'scan', listener: (result: string) => void): this;\n off(event: 'scan', listener: (result: string) => void): this;\n}\n\nclass QrCodeScanner extends EventEmitter {\n _height?: number;\n _width?: number;\n _camera: Camera;\n _videoElement: HTMLVideoElement;\n _canvas: HTMLCanvasElement;\n _ctx: CanvasRenderingContext2D;\n _intervalTime: number | null = null;\n _intervalTick?: number;\n _isStarted = false;\n\n\n constructor(camera: Camera) {\n super();\n\n this._camera = camera;\n this._videoElement = camera.videoElement;\n\n\n this._canvas = document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;\n\n this._camera.on('started', () => this._tryStart());\n this._camera.on('stopped', () => this._stop());\n }\n\n start(intervalTime = 500) {\n this._intervalTime = intervalTime;\n\n if (this._isStarted) {\n return;\n }\n this._isStarted = true;\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 !== '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\n this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime as number);\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 scanResultPromise = QrScanner.scanImage(this._canvas, {\n returnDetailedScanResult: true,\n });\n\n scanResultPromise.then((result) => {\n this.emit('scan', result.data);\n }).catch((e) => {\n // do nothing\n });\n }\n}\n\nexport default QrCodeScanner;\n"],"names":["SharedCameras"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -4,10 +4,9 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { BarcodeFormat, MultiFormatReader, DecodeHintType, BinaryBitmap, HybridBinarizer, RGBLuminanceSource } from "@zxing/library";
|
|
8
|
-
import { BarcodeFormat as BarcodeFormat2 } from "@zxing/library";
|
|
9
7
|
import EventEmitter from "events";
|
|
10
8
|
import Logger from "@wemap/logger";
|
|
9
|
+
import QrScanner from "qr-scanner";
|
|
11
10
|
class SharedCameras extends EventEmitter {
|
|
12
11
|
constructor() {
|
|
13
12
|
super(...arguments);
|
|
@@ -327,37 +326,31 @@ __publicField(_Camera, "DEFAULT_OPTIONS", {
|
|
|
327
326
|
});
|
|
328
327
|
__publicField(_Camera, "GENERIC_HARDWARE_VERTICAL_FOV", 60);
|
|
329
328
|
let Camera = _Camera;
|
|
330
|
-
|
|
329
|
+
class QrCodeScanner extends EventEmitter {
|
|
331
330
|
constructor(camera) {
|
|
332
331
|
super();
|
|
333
332
|
__publicField(this, "_height");
|
|
334
|
-
__publicField(this, "_luminances");
|
|
335
333
|
__publicField(this, "_width");
|
|
336
334
|
__publicField(this, "_camera");
|
|
337
335
|
__publicField(this, "_videoElement");
|
|
338
336
|
__publicField(this, "_canvas");
|
|
339
337
|
__publicField(this, "_ctx");
|
|
340
|
-
__publicField(this, "_codeReader");
|
|
341
338
|
__publicField(this, "_intervalTime", null);
|
|
342
339
|
__publicField(this, "_intervalTick");
|
|
343
340
|
__publicField(this, "_isStarted", false);
|
|
344
341
|
this._camera = camera;
|
|
345
342
|
this._videoElement = camera.videoElement;
|
|
346
|
-
this._codeReader = new MultiFormatReader();
|
|
347
343
|
this._canvas = document.createElement("canvas");
|
|
348
344
|
this._ctx = this._canvas.getContext("2d");
|
|
349
345
|
this._camera.on("started", () => this._tryStart());
|
|
350
346
|
this._camera.on("stopped", () => this._stop());
|
|
351
347
|
}
|
|
352
|
-
start(intervalTime = 500
|
|
348
|
+
start(intervalTime = 500) {
|
|
353
349
|
this._intervalTime = intervalTime;
|
|
354
350
|
if (this._isStarted) {
|
|
355
351
|
return;
|
|
356
352
|
}
|
|
357
353
|
this._isStarted = true;
|
|
358
|
-
const hints = /* @__PURE__ */ new Map();
|
|
359
|
-
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
|
|
360
|
-
this._codeReader.setHints(hints);
|
|
361
354
|
this._tryStart();
|
|
362
355
|
}
|
|
363
356
|
stop() {
|
|
@@ -372,41 +365,26 @@ const _QrCodeScanner = class _QrCodeScanner extends EventEmitter {
|
|
|
372
365
|
this._height = this._videoElement.videoHeight;
|
|
373
366
|
this._canvas.width = this._width;
|
|
374
367
|
this._canvas.height = this._height;
|
|
375
|
-
this._luminances = new Uint8ClampedArray(this._width * this._height);
|
|
376
368
|
this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime);
|
|
377
369
|
}
|
|
378
370
|
_stop() {
|
|
379
371
|
clearInterval(this._intervalTick);
|
|
380
372
|
}
|
|
381
373
|
_onFrame() {
|
|
382
|
-
if (!this._width || !this._height
|
|
374
|
+
if (!this._width || !this._height) {
|
|
383
375
|
return;
|
|
384
376
|
}
|
|
385
377
|
this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
)
|
|
394
|
-
);
|
|
395
|
-
try {
|
|
396
|
-
const result = this._codeReader.decodeWithState(binaryBitmap);
|
|
397
|
-
this.emit("scan", result.getText());
|
|
398
|
-
} catch (e) {
|
|
399
|
-
}
|
|
378
|
+
const scanResultPromise = QrScanner.scanImage(this._canvas, {
|
|
379
|
+
returnDetailedScanResult: true
|
|
380
|
+
});
|
|
381
|
+
scanResultPromise.then((result) => {
|
|
382
|
+
this.emit("scan", result.data);
|
|
383
|
+
}).catch((e) => {
|
|
384
|
+
});
|
|
400
385
|
}
|
|
401
|
-
}
|
|
402
|
-
__publicField(_QrCodeScanner, "DEFAULT_FORMATS", [
|
|
403
|
-
BarcodeFormat.QR_CODE,
|
|
404
|
-
BarcodeFormat.DATA_MATRIX,
|
|
405
|
-
BarcodeFormat.CODABAR
|
|
406
|
-
]);
|
|
407
|
-
let QrCodeScanner = _QrCodeScanner;
|
|
386
|
+
}
|
|
408
387
|
export {
|
|
409
|
-
BarcodeFormat2 as BarcodeFormat,
|
|
410
388
|
Camera,
|
|
411
389
|
QrCodeScanner,
|
|
412
390
|
SharedCameras$1 as SharedCameras,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/SharedCameras.ts","../src/CameraUtils.ts","../src/Camera.ts","../src/QrCodeScanner.ts"],"sourcesContent":["import EventEmitter from 'events';\n\nimport Camera from './Camera.js';\n\ndeclare interface SharedCameras {\n on(event: 'added', listener: (obj: CameraContainer) => void): this;\n on(event: 'removed', listener: (obj: { camera: Camera }) => void): this;\n}\n\ntype CameraContainer = { container: HTMLElement, camera: Camera };\n\nclass SharedCameras extends EventEmitter {\n\n _list: CameraContainer[] = [];\n\n _add(camera: Camera, container: HTMLElement) {\n const obj = {\n camera,\n container\n };\n this._list.push(obj);\n this.emit('added', obj);\n }\n\n _remove(camera: Camera) {\n this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);\n this.emit('removed', { camera });\n }\n\n get list() {\n return this._list;\n }\n\n getCameraByContainer(container: HTMLElement) {\n return this._list.find(obj => obj.container === container)?.camera || null;\n }\n}\n\nexport default new SharedCameras();\n","export type IntrinsicParams = [number, number, number, number, number, number, number, number, number];\nexport type DistortionsParams = [number, number, number, number, number];\n\nexport type Calibration = {\n intrinsic: IntrinsicParams,\n distortions?: DistortionsParams\n}\n\nexport function convertFov(angle: number, aspectRatio: number) {\n return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;\n}\n\nexport function calcDisplayedFov(\n videoContainer: HTMLElement,\n videoElement: HTMLVideoElement,\n hardwareVerticalFov: number\n) {\n\n const sourceWidth = videoElement.videoWidth;\n const sourceHeight = videoElement.videoHeight;\n\n if (!sourceWidth || !sourceHeight) {\n return null;\n }\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 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n */\nexport function canvasToBase64(canvas: HTMLCanvasElement, type = 'image/png', quality: any = null) {\n return canvas\n .toDataURL(type, quality)\n .substring(('data:' + type + ';base64,').length);\n}\n\nexport function base64ToCanvas(base64: string, type = 'image/png') {\n if (type !== 'image/png') {\n throw new Error('Only image/png is supported');\n }\n const canvas = document.createElement('canvas');\n const image = new Image();\n image.src = 'data:image/png;base64,' + base64;\n canvas.width = image.width;\n canvas.height = image.height;\n canvas.getContext('2d')?.drawImage(image, 0, 0);\n return canvas;\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(\n width: number,\n height: number,\n fovOfWidth: number\n): Calibration {\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: HTMLCanvasElement) {\n const ctx = imageCanvas.getContext('2d');\n if (!ctx) return;\n\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 = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;\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 */\nexport function reduceImageSize(\n imageCanvas: HTMLCanvasElement,\n maxWidth: number,\n maxHeight?: number\n) {\n\n let newWidth = imageCanvas.width;\n let newHeight = imageCanvas.height;\n\n if (typeof maxHeight !== 'undefined') {\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\nexport function htmlImageToCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n canvas.width = image.width;\n canvas.height = image.height;\n const context = canvas.getContext('2d');\n context?.scale(canvas.width / image.width, canvas.height / image.height);\n context?.drawImage(image, 0, 0);\n return canvas;\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.\n\ntype CameraOptions = {\n width?: number,\n height?: number,\n resizeOnWindowChange?: boolean\n};\n\nexport type CameraState = 'stopped' | 'starting' | 'started' | 'stopping';\n\ndeclare interface Camera {\n on(event: 'starting', listener: () => void): this;\n on(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n on(event: 'stopping', listener: () => void): this;\n on(event: 'stopped', listener: () => void): this;\n on(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n off(event: 'starting', listener: () => void): this;\n off(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n off(event: 'stopping', listener: () => void): this;\n off(event: 'stopped', listener: () => void): this;\n off(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n}\n\n\nclass Camera extends EventEmitter {\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 _userMediaConstraints: MediaStreamConstraints;\n videoStream: MediaStream | null = null;\n\n videoContainer: HTMLElement;\n videoElement: HTMLVideoElement;\n\n fov: {horizontal: number, vertical: number} | null = null;\n\n _state: CameraState = 'stopped';\n\n _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;\n _resizeOnWindowChange;\n\n constructor(container: HTMLElement, options: CameraOptions = {}) {\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 } as any // For resizeMode\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?: MediaTrackConstraints) {\n\n if (this._state === 'started' || this._state === 'starting') {\n throw new Error('Camera is already started');\n }\n\n if (this._state === 'stopping') {\n await new Promise(resolve => this.once('stopped', resolve));\n }\n\n this._state = 'starting';\n this.emit('starting');\n\n await Camera.checkAvailability();\n\n if (typeof videoMediaConstraints === 'object') {\n Object.assign(this._userMediaConstraints.video as MediaTrackConstraints,\n this._userMediaConstraints.video as MediaTrackConstraints, 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 = 'started';\n const output = {\n videoElement: this.videoElement,\n stream\n };\n\n this.emit('started', output);\n return output;\n }\n\n async stop() {\n\n if (this._state === 'stopped') {\n throw new Error('Camera is already stopped');\n }\n\n if (this._state === 'starting') {\n await new Promise(resolve => this.once('started', resolve));\n }\n\n this._state = 'stopping';\n this.emit('stopping');\n\n if (this.videoStream) {\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 = 'stopped';\n this.emit('stopped');\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 stream.getVideoTracks().forEach(track => track.stop());\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 if (['stopping', 'stopped'].includes(this.state)) {\n return;\n }\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 get currentImage() {\n\n if (this._state !== 'started') {\n Logger.warn('Trying to access image but video is not started');\n return Promise.resolve(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","import 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\ndeclare interface QrCodeScanner {\n on(event: 'scan', listener: (result: string) => void): this;\n off(event: 'scan', listener: (result: string) => void): this;\n}\n\nclass QrCodeScanner extends EventEmitter {\n\n static DEFAULT_FORMATS: BarcodeFormat[] = [\n BarcodeFormat.QR_CODE,\n BarcodeFormat.DATA_MATRIX,\n BarcodeFormat.CODABAR\n ];\n\n _height?: number;\n _luminances?: Uint8ClampedArray;\n _width?: number;\n _camera: Camera;\n _videoElement: HTMLVideoElement;\n _canvas: HTMLCanvasElement;\n _ctx: CanvasRenderingContext2D;\n _codeReader: MultiFormatReader;\n _intervalTime: number | null = null;\n _intervalTick?: number;\n _isStarted = false;\n\n\n constructor(camera: 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') as CanvasRenderingContext2D;\n\n this._camera.on('started', () => this._tryStart());\n this._camera.on('stopped', () => this._stop());\n }\n\n start(intervalTime = 500, formats: BarcodeFormat[] = 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 !== '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 Uint8ClampedArray(this._width * this._height);\n\n this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime as number);\n }\n\n _stop() {\n clearInterval(this._intervalTick);\n }\n\n _onFrame() {\n\n if (!this._width || !this._height || !this._luminances) {\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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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/SharedCameras.ts","../src/CameraUtils.ts","../src/Camera.ts","../src/QrCodeScanner.ts"],"sourcesContent":["import EventEmitter from 'events';\n\nimport Camera from './Camera.js';\n\ndeclare interface SharedCameras {\n on(event: 'added', listener: (obj: CameraContainer) => void): this;\n on(event: 'removed', listener: (obj: { camera: Camera }) => void): this;\n}\n\ntype CameraContainer = { container: HTMLElement, camera: Camera };\n\nclass SharedCameras extends EventEmitter {\n\n _list: CameraContainer[] = [];\n\n _add(camera: Camera, container: HTMLElement) {\n const obj = {\n camera,\n container\n };\n this._list.push(obj);\n this.emit('added', obj);\n }\n\n _remove(camera: Camera) {\n this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);\n this.emit('removed', { camera });\n }\n\n get list() {\n return this._list;\n }\n\n getCameraByContainer(container: HTMLElement) {\n return this._list.find(obj => obj.container === container)?.camera || null;\n }\n}\n\nexport default new SharedCameras();\n","export type IntrinsicParams = [number, number, number, number, number, number, number, number, number];\nexport type DistortionsParams = [number, number, number, number, number];\n\nexport type Calibration = {\n intrinsic: IntrinsicParams,\n distortions?: DistortionsParams\n}\n\nexport function convertFov(angle: number, aspectRatio: number) {\n return 2 * Math.atan(Math.tan((angle / 180 * Math.PI) / 2) * aspectRatio) * 180 / Math.PI;\n}\n\nexport function calcDisplayedFov(\n videoContainer: HTMLElement,\n videoElement: HTMLVideoElement,\n hardwareVerticalFov: number\n) {\n\n const sourceWidth = videoElement.videoWidth;\n const sourceHeight = videoElement.videoHeight;\n\n if (!sourceWidth || !sourceHeight) {\n return null;\n }\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 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\n */\nexport function canvasToBase64(canvas: HTMLCanvasElement, type = 'image/png', quality: any = null) {\n return canvas\n .toDataURL(type, quality)\n .substring(('data:' + type + ';base64,').length);\n}\n\nexport function base64ToCanvas(base64: string, type = 'image/png') {\n if (type !== 'image/png') {\n throw new Error('Only image/png is supported');\n }\n const canvas = document.createElement('canvas');\n const image = new Image();\n image.src = 'data:image/png;base64,' + base64;\n canvas.width = image.width;\n canvas.height = image.height;\n canvas.getContext('2d')?.drawImage(image, 0, 0);\n return canvas;\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(\n width: number,\n height: number,\n fovOfWidth: number\n): Calibration {\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: HTMLCanvasElement) {\n const ctx = imageCanvas.getContext('2d');\n if (!ctx) return;\n\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 = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;\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 */\nexport function reduceImageSize(\n imageCanvas: HTMLCanvasElement,\n maxWidth: number,\n maxHeight?: number\n) {\n\n let newWidth = imageCanvas.width;\n let newHeight = imageCanvas.height;\n\n if (typeof maxHeight !== 'undefined') {\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\nexport function htmlImageToCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n canvas.width = image.width;\n canvas.height = image.height;\n const context = canvas.getContext('2d');\n context?.scale(canvas.width / image.width, canvas.height / image.height);\n context?.drawImage(image, 0, 0);\n return canvas;\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.\n\ntype CameraOptions = {\n width?: number,\n height?: number,\n resizeOnWindowChange?: boolean\n};\n\nexport type CameraState = 'stopped' | 'starting' | 'started' | 'stopping';\n\ndeclare interface Camera {\n on(event: 'starting', listener: () => void): this;\n on(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n on(event: 'stopping', listener: () => void): this;\n on(event: 'stopped', listener: () => void): this;\n on(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n off(event: 'starting', listener: () => void): this;\n off(event: 'started', listener: (obj: ({ videoElement: HTMLVideoElement, stream: MediaStream })) => void): this;\n off(event: 'stopping', listener: () => void): this;\n off(event: 'stopped', listener: () => void): this;\n off(event: 'fov.changed', listener: (obj: ({ vertical: number, horizontal: number })) => void): this;\n}\n\n\nclass Camera extends EventEmitter {\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 _userMediaConstraints: MediaStreamConstraints;\n videoStream: MediaStream | null = null;\n\n videoContainer: HTMLElement;\n videoElement: HTMLVideoElement;\n\n fov: {horizontal: number, vertical: number} | null = null;\n\n _state: CameraState = 'stopped';\n\n _hardwareVerticalFov = Camera.GENERIC_HARDWARE_VERTICAL_FOV;\n _resizeOnWindowChange;\n\n constructor(container: HTMLElement, options: CameraOptions = {}) {\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 } as any // For resizeMode\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?: MediaTrackConstraints) {\n\n if (this._state === 'started' || this._state === 'starting') {\n throw new Error('Camera is already started');\n }\n\n if (this._state === 'stopping') {\n await new Promise(resolve => this.once('stopped', resolve));\n }\n\n this._state = 'starting';\n this.emit('starting');\n\n await Camera.checkAvailability();\n\n if (typeof videoMediaConstraints === 'object') {\n Object.assign(this._userMediaConstraints.video as MediaTrackConstraints,\n this._userMediaConstraints.video as MediaTrackConstraints, 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 = 'started';\n const output = {\n videoElement: this.videoElement,\n stream\n };\n\n this.emit('started', output);\n return output;\n }\n\n async stop() {\n\n if (this._state === 'stopped') {\n throw new Error('Camera is already stopped');\n }\n\n if (this._state === 'starting') {\n await new Promise(resolve => this.once('started', resolve));\n }\n\n this._state = 'stopping';\n this.emit('stopping');\n\n if (this.videoStream) {\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 = 'stopped';\n this.emit('stopped');\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 stream.getVideoTracks().forEach(track => track.stop());\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 if (['stopping', 'stopped'].includes(this.state)) {\n return;\n }\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 get currentImage() {\n\n if (this._state !== 'started') {\n Logger.warn('Trying to access image but video is not started');\n return Promise.resolve(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","import EventEmitter from 'events';\nimport QrScanner from 'qr-scanner';\n\nimport Camera from './Camera.js';\n\ndeclare interface QrCodeScanner {\n on(event: 'scan', listener: (result: string) => void): this;\n off(event: 'scan', listener: (result: string) => void): this;\n}\n\nclass QrCodeScanner extends EventEmitter {\n _height?: number;\n _width?: number;\n _camera: Camera;\n _videoElement: HTMLVideoElement;\n _canvas: HTMLCanvasElement;\n _ctx: CanvasRenderingContext2D;\n _intervalTime: number | null = null;\n _intervalTick?: number;\n _isStarted = false;\n\n\n constructor(camera: Camera) {\n super();\n\n this._camera = camera;\n this._videoElement = camera.videoElement;\n\n\n this._canvas = document.createElement('canvas');\n this._ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;\n\n this._camera.on('started', () => this._tryStart());\n this._camera.on('stopped', () => this._stop());\n }\n\n start(intervalTime = 500) {\n this._intervalTime = intervalTime;\n\n if (this._isStarted) {\n return;\n }\n this._isStarted = true;\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 !== '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\n this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime as number);\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 scanResultPromise = QrScanner.scanImage(this._canvas, {\n returnDetailedScanResult: true,\n });\n\n scanResultPromise.then((result) => {\n this.emit('scan', result.data);\n }).catch((e) => {\n // do nothing\n });\n }\n}\n\nexport default QrCodeScanner;\n"],"names":["SharedCameras"],"mappings":"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"}
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"directory": "packages/camera"
|
|
13
13
|
},
|
|
14
14
|
"name": "@wemap/camera",
|
|
15
|
-
"version": "12.
|
|
15
|
+
"version": "12.10.6",
|
|
16
16
|
"bugs": {
|
|
17
17
|
"url": "https://github.com/wemap/wemap-modules-js/issues"
|
|
18
18
|
},
|
|
@@ -29,16 +29,17 @@
|
|
|
29
29
|
"license": "ISC",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@types/events": "^3.0.0",
|
|
32
|
-
"@wemap/logger": "^12.
|
|
33
|
-
"
|
|
34
|
-
"
|
|
32
|
+
"@wemap/logger": "^12.10.0",
|
|
33
|
+
"events": "^3.3.0",
|
|
34
|
+
"qr-scanner": "^1.4.2"
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"exports": {
|
|
38
38
|
".": {
|
|
39
|
+
"types": "./index.ts",
|
|
39
40
|
"import": "./dist/index.mjs",
|
|
40
41
|
"require": "./dist/index.js"
|
|
41
42
|
}
|
|
42
43
|
},
|
|
43
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "dbedc4948fc6e9f26a93e266a2fcb71abb4cc764"
|
|
44
45
|
}
|
package/src/QrCodeScanner.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
|
-
import
|
|
3
|
-
MultiFormatReader,
|
|
4
|
-
BarcodeFormat,
|
|
5
|
-
DecodeHintType,
|
|
6
|
-
RGBLuminanceSource,
|
|
7
|
-
BinaryBitmap,
|
|
8
|
-
HybridBinarizer
|
|
9
|
-
} from '@zxing/library';
|
|
2
|
+
import QrScanner from 'qr-scanner';
|
|
10
3
|
|
|
11
4
|
import Camera from './Camera.js';
|
|
12
5
|
|
|
@@ -16,21 +9,12 @@ declare interface QrCodeScanner {
|
|
|
16
9
|
}
|
|
17
10
|
|
|
18
11
|
class QrCodeScanner extends EventEmitter {
|
|
19
|
-
|
|
20
|
-
static DEFAULT_FORMATS: BarcodeFormat[] = [
|
|
21
|
-
BarcodeFormat.QR_CODE,
|
|
22
|
-
BarcodeFormat.DATA_MATRIX,
|
|
23
|
-
BarcodeFormat.CODABAR
|
|
24
|
-
];
|
|
25
|
-
|
|
26
12
|
_height?: number;
|
|
27
|
-
_luminances?: Uint8ClampedArray;
|
|
28
13
|
_width?: number;
|
|
29
14
|
_camera: Camera;
|
|
30
15
|
_videoElement: HTMLVideoElement;
|
|
31
16
|
_canvas: HTMLCanvasElement;
|
|
32
17
|
_ctx: CanvasRenderingContext2D;
|
|
33
|
-
_codeReader: MultiFormatReader;
|
|
34
18
|
_intervalTime: number | null = null;
|
|
35
19
|
_intervalTick?: number;
|
|
36
20
|
_isStarted = false;
|
|
@@ -42,7 +26,6 @@ class QrCodeScanner extends EventEmitter {
|
|
|
42
26
|
this._camera = camera;
|
|
43
27
|
this._videoElement = camera.videoElement;
|
|
44
28
|
|
|
45
|
-
this._codeReader = new MultiFormatReader();
|
|
46
29
|
|
|
47
30
|
this._canvas = document.createElement('canvas');
|
|
48
31
|
this._ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
@@ -51,7 +34,7 @@ class QrCodeScanner extends EventEmitter {
|
|
|
51
34
|
this._camera.on('stopped', () => this._stop());
|
|
52
35
|
}
|
|
53
36
|
|
|
54
|
-
start(intervalTime = 500
|
|
37
|
+
start(intervalTime = 500) {
|
|
55
38
|
this._intervalTime = intervalTime;
|
|
56
39
|
|
|
57
40
|
if (this._isStarted) {
|
|
@@ -59,10 +42,6 @@ class QrCodeScanner extends EventEmitter {
|
|
|
59
42
|
}
|
|
60
43
|
this._isStarted = true;
|
|
61
44
|
|
|
62
|
-
const hints = new Map();
|
|
63
|
-
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
|
|
64
|
-
this._codeReader.setHints(hints);
|
|
65
|
-
|
|
66
45
|
this._tryStart();
|
|
67
46
|
}
|
|
68
47
|
|
|
@@ -81,7 +60,6 @@ class QrCodeScanner extends EventEmitter {
|
|
|
81
60
|
this._height = this._videoElement.videoHeight;
|
|
82
61
|
this._canvas.width = this._width;
|
|
83
62
|
this._canvas.height = this._height;
|
|
84
|
-
this._luminances = new Uint8ClampedArray(this._width * this._height);
|
|
85
63
|
|
|
86
64
|
this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime as number);
|
|
87
65
|
}
|
|
@@ -92,30 +70,20 @@ class QrCodeScanner extends EventEmitter {
|
|
|
92
70
|
|
|
93
71
|
_onFrame() {
|
|
94
72
|
|
|
95
|
-
if (!this._width || !this._height
|
|
73
|
+
if (!this._width || !this._height) {
|
|
96
74
|
return;
|
|
97
75
|
}
|
|
98
76
|
|
|
99
77
|
this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
|
|
100
|
-
const
|
|
78
|
+
const scanResultPromise = QrScanner.scanImage(this._canvas, {
|
|
79
|
+
returnDetailedScanResult: true,
|
|
80
|
+
});
|
|
101
81
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const binaryBitmap = new BinaryBitmap(
|
|
108
|
-
new HybridBinarizer(
|
|
109
|
-
new RGBLuminanceSource(this._luminances, this._width, this._height)
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
const result = this._codeReader.decodeWithState(binaryBitmap);
|
|
115
|
-
this.emit('scan', result.getText());
|
|
116
|
-
} catch (e) {
|
|
82
|
+
scanResultPromise.then((result) => {
|
|
83
|
+
this.emit('scan', result.data);
|
|
84
|
+
}).catch((e) => {
|
|
117
85
|
// do nothing
|
|
118
|
-
}
|
|
86
|
+
});
|
|
119
87
|
}
|
|
120
88
|
}
|
|
121
89
|
|