@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 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
- const _QrCodeScanner = class _QrCodeScanner extends EventEmitter {
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, formats = _QrCodeScanner.DEFAULT_FORMATS) {
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 || !this._luminances) {
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 img = this._ctx.getImageData(0, 0, this._width, this._height);
388
- for (let i = 0; i < this._luminances.length; i++) {
389
- this._luminances[i] = (img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4 & 255;
390
- }
391
- const binaryBitmap = new library.BinaryBitmap(
392
- new library.HybridBinarizer(
393
- new library.RGBLuminanceSource(this._luminances, this._width, this._height)
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
- const _QrCodeScanner = class _QrCodeScanner extends EventEmitter {
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, formats = _QrCodeScanner.DEFAULT_FORMATS) {
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 || !this._luminances) {
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 img = this._ctx.getImageData(0, 0, this._width, this._height);
387
- for (let i = 0; i < this._luminances.length; i++) {
388
- this._luminances[i] = (img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4 & 255;
389
- }
390
- const binaryBitmap = new BinaryBitmap(
391
- new HybridBinarizer(
392
- new RGBLuminanceSource(this._luminances, this._width, this._height)
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,
@@ -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
@@ -1,4 +1,3 @@
1
- export { BarcodeFormat } from '@zxing/library';
2
1
  export { default as Camera, type CameraState } from './src/Camera.js';
3
2
  export { default as QrCodeScanner } from './src/QrCodeScanner.js';
4
3
  export { default as SharedCameras } from './src/SharedCameras.js';
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/camera"
13
13
  },
14
14
  "name": "@wemap/camera",
15
- "version": "12.0.0",
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.0.0",
33
- "@zxing/library": "^0.18.4",
34
- "events": "^3.3.0"
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": "90961b51b2bbbb32c4e7b64ddf19097e1d44a1df"
44
+ "gitHead": "dbedc4948fc6e9f26a93e266a2fcb71abb4cc764"
44
45
  }
@@ -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, formats: BarcodeFormat[] = QrCodeScanner.DEFAULT_FORMATS) {
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 || !this._luminances) {
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 img = this._ctx.getImageData(0, 0, this._width, this._height);
78
+ const scanResultPromise = QrScanner.scanImage(this._canvas, {
79
+ returnDetailedScanResult: true,
80
+ });
101
81
 
102
- for (let i = 0; i < this._luminances.length; i++) {
103
- // eslint-disable-next-line no-bitwise
104
- this._luminances[i] = ((img.data[i * 4] + img.data[i * 4 + 1] * 2 + img.data[i * 4 + 2]) / 4) & 0xFF;
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