@wemap/camera 12.10.6 → 12.10.8-alpha.2

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
@@ -1,400 +1,2 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
- var __publicField = (obj, key, value) => {
5
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
- return value;
7
- };
8
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
9
- const EventEmitter = require("events");
10
- const Logger = require("@wemap/logger");
11
- const QrScanner = require("qr-scanner");
12
- class SharedCameras extends EventEmitter {
13
- constructor() {
14
- super(...arguments);
15
- __publicField(this, "_list", []);
16
- }
17
- _add(camera, container) {
18
- const obj = {
19
- camera,
20
- container
21
- };
22
- this._list.push(obj);
23
- this.emit("added", obj);
24
- }
25
- _remove(camera) {
26
- this._list = this._list.filter(({ camera: _camera }) => _camera !== camera);
27
- this.emit("removed", { camera });
28
- }
29
- get list() {
30
- return this._list;
31
- }
32
- getCameraByContainer(container) {
33
- var _a;
34
- return ((_a = this._list.find((obj) => obj.container === container)) == null ? void 0 : _a.camera) || null;
35
- }
36
- }
37
- const SharedCameras$1 = new SharedCameras();
38
- function convertFov(angle, aspectRatio) {
39
- return 2 * Math.atan(Math.tan(angle / 180 * Math.PI / 2) * aspectRatio) * 180 / Math.PI;
40
- }
41
- function calcDisplayedFov(videoContainer, videoElement, hardwareVerticalFov) {
42
- const sourceWidth = videoElement.videoWidth;
43
- const sourceHeight = videoElement.videoHeight;
44
- if (!sourceWidth || !sourceHeight) {
45
- return null;
46
- }
47
- const containerWidth = videoContainer.offsetWidth;
48
- const containerHeight = videoContainer.offsetHeight;
49
- const sourceAspect = sourceWidth / sourceHeight;
50
- const screenAspect = containerWidth / containerHeight;
51
- let fovV = hardwareVerticalFov;
52
- let fovH = convertFov(fovV, sourceAspect);
53
- if (screenAspect < sourceAspect) {
54
- fovH = convertFov(fovV, screenAspect);
55
- } else {
56
- fovV = convertFov(fovH, 1 / screenAspect);
57
- }
58
- return {
59
- vertical: fovV,
60
- horizontal: fovH
61
- };
62
- }
63
- function canvasToBase64(canvas, type = "image/png", quality = null) {
64
- return canvas.toDataURL(type, quality).substring(("data:" + type + ";base64,").length);
65
- }
66
- function base64ToCanvas(base64, type = "image/png") {
67
- var _a;
68
- if (type !== "image/png") {
69
- throw new Error("Only image/png is supported");
70
- }
71
- const canvas = document.createElement("canvas");
72
- const image = new Image();
73
- image.src = "data:image/png;base64," + base64;
74
- canvas.width = image.width;
75
- canvas.height = image.height;
76
- (_a = canvas.getContext("2d")) == null ? void 0 : _a.drawImage(image, 0, 0);
77
- return canvas;
78
- }
79
- function createCameraCalibrationFromWidthHeightFov(width, height, fovOfWidth) {
80
- const fovOfHeight = 2 * Math.atan(height * Math.tan(fovOfWidth / 2) / width);
81
- const fx = width / (2 * Math.tan(0.5 * fovOfWidth));
82
- const fy = height / (2 * Math.tan(0.5 * fovOfHeight));
83
- const cx = width / 2;
84
- const cy = height / 2;
85
- return {
86
- intrinsic: [fx, 0, cx, 0, fy, cy, 0, 0, 1],
87
- distortions: [0, 0, 0, 0, 0]
88
- };
89
- }
90
- function convertToGrayscale(imageCanvas) {
91
- const ctx = imageCanvas.getContext("2d");
92
- if (!ctx)
93
- return;
94
- const imgData = ctx.getImageData(0, 0, imageCanvas.width, imageCanvas.height);
95
- const pixels = imgData.data;
96
- for (let i = 0; i < pixels.length; i += 4) {
97
- const lightness = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
98
- pixels[i] = lightness;
99
- pixels[i + 1] = lightness;
100
- pixels[i + 2] = lightness;
101
- }
102
- ctx.putImageData(imgData, 0, 0);
103
- }
104
- function reduceImageSize(imageCanvas, maxWidth, maxHeight) {
105
- let newWidth = imageCanvas.width;
106
- let newHeight = imageCanvas.height;
107
- if (typeof maxHeight !== "undefined") {
108
- if (newWidth > maxWidth) {
109
- newHeight *= maxWidth / newWidth;
110
- newWidth = maxWidth;
111
- }
112
- if (newHeight > maxHeight) {
113
- newWidth *= maxHeight / newHeight;
114
- newHeight = maxHeight;
115
- }
116
- } else {
117
- const aspectRatio = imageCanvas.width / imageCanvas.height;
118
- if (imageCanvas.width > imageCanvas.height) {
119
- if (imageCanvas.width > maxWidth) {
120
- newWidth = maxWidth;
121
- newHeight = maxWidth / aspectRatio;
122
- }
123
- } else if (imageCanvas.height > maxWidth) {
124
- newHeight = maxWidth;
125
- newWidth = maxWidth * aspectRatio;
126
- }
127
- }
128
- if (newWidth === imageCanvas.width && newHeight === imageCanvas.height) {
129
- return imageCanvas;
130
- }
131
- const newCanvas = document.createElement("canvas");
132
- const newContext = newCanvas.getContext("2d");
133
- newCanvas.width = newWidth;
134
- newCanvas.height = newHeight;
135
- newContext == null ? void 0 : newContext.drawImage(imageCanvas, 0, 0, newCanvas.width, newCanvas.height);
136
- return newCanvas;
137
- }
138
- function htmlImageToCanvas(image) {
139
- const canvas = document.createElement("canvas");
140
- canvas.width = image.width;
141
- canvas.height = image.height;
142
- const context = canvas.getContext("2d");
143
- context == null ? void 0 : context.scale(canvas.width / image.width, canvas.height / image.height);
144
- context == null ? void 0 : context.drawImage(image, 0, 0);
145
- return canvas;
146
- }
147
- const _Camera = class _Camera extends EventEmitter {
148
- constructor(container, options = {}) {
149
- super();
150
- __publicField(this, "_userMediaConstraints");
151
- __publicField(this, "videoStream", null);
152
- __publicField(this, "videoContainer");
153
- __publicField(this, "videoElement");
154
- __publicField(this, "fov", null);
155
- __publicField(this, "_state", "stopped");
156
- __publicField(this, "_hardwareVerticalFov", _Camera.GENERIC_HARDWARE_VERTICAL_FOV);
157
- __publicField(this, "_resizeOnWindowChange");
158
- __publicField(this, "notifyContainerSizeChanged", () => {
159
- this._resizeElement();
160
- this._computeFov();
161
- });
162
- __publicField(this, "_resizeElement", () => {
163
- if (!this.videoElement) {
164
- throw new Error("No video element found");
165
- }
166
- const sourceWidth = this.videoElement.videoWidth;
167
- const sourceHeight = this.videoElement.videoHeight;
168
- const containerWidth = this.videoContainer.offsetWidth;
169
- const containerHeight = this.videoContainer.offsetHeight;
170
- const sourceAspect = sourceWidth / sourceHeight;
171
- const screenAspect = containerWidth / containerHeight;
172
- if (screenAspect < sourceAspect) {
173
- const newWidth = sourceAspect * containerHeight;
174
- this.videoElement.style.width = newWidth + "px";
175
- this.videoElement.style.marginLeft = -(newWidth - containerWidth) / 2 + "px";
176
- this.videoElement.style.height = containerHeight + "px";
177
- this.videoElement.style.marginTop = "0px";
178
- } else {
179
- const newHeight = 1 / (sourceAspect / containerWidth);
180
- this.videoElement.style.height = newHeight + "px";
181
- this.videoElement.style.marginTop = -(newHeight - containerHeight) / 2 + "px";
182
- this.videoElement.style.width = containerWidth + "px";
183
- this.videoElement.style.marginLeft = "0px";
184
- }
185
- });
186
- __publicField(this, "_computeFov", () => {
187
- if (["stopping", "stopped"].includes(this.state)) {
188
- return;
189
- }
190
- this.fov = calcDisplayedFov(
191
- this.videoContainer,
192
- this.videoElement,
193
- this._hardwareVerticalFov
194
- );
195
- this.emit("fov.changed", this.fov);
196
- });
197
- options = Object.assign({}, _Camera.DEFAULT_OPTIONS, options);
198
- SharedCameras$1._add(this, container);
199
- this.videoContainer = container;
200
- this._resizeOnWindowChange = options.resizeOnWindowChange;
201
- this._userMediaConstraints = {
202
- audio: false,
203
- video: {
204
- facingMode: "environment",
205
- width: { ideal: options.width },
206
- height: { ideal: options.height },
207
- resizeMode: "none"
208
- }
209
- // For resizeMode
210
- };
211
- this.videoContainer.style.overflow = "hidden";
212
- this.videoElement = document.createElement("video");
213
- this.videoElement.setAttribute("id", "wemap-camera");
214
- this.videoElement.setAttribute("preload", "none");
215
- this.videoElement.setAttribute("muted", "");
216
- this.videoElement.setAttribute("playsinline", "");
217
- this.videoContainer.appendChild(this.videoElement);
218
- }
219
- async start(videoMediaConstraints) {
220
- if (this._state === "started" || this._state === "starting") {
221
- throw new Error("Camera is already started");
222
- }
223
- if (this._state === "stopping") {
224
- await new Promise((resolve) => this.once("stopped", resolve));
225
- }
226
- this._state = "starting";
227
- this.emit("starting");
228
- await _Camera.checkAvailability();
229
- if (typeof videoMediaConstraints === "object") {
230
- Object.assign(
231
- this._userMediaConstraints.video,
232
- this._userMediaConstraints.video,
233
- videoMediaConstraints
234
- );
235
- }
236
- const stream = await navigator.mediaDevices.getUserMedia(this._userMediaConstraints);
237
- this.videoElement.oncanplaythrough = () => this.videoElement.play();
238
- const metadataPr = new Promise((resolve) => this.videoElement.onloadedmetadata = resolve);
239
- this.videoElement.srcObject = this.videoStream = stream;
240
- await metadataPr;
241
- this._resizeElement();
242
- this._computeFov();
243
- if (this._resizeOnWindowChange) {
244
- window.addEventListener("resize", this.notifyContainerSizeChanged);
245
- }
246
- this._state = "started";
247
- const output = {
248
- videoElement: this.videoElement,
249
- stream
250
- };
251
- this.emit("started", output);
252
- return output;
253
- }
254
- async stop() {
255
- if (this._state === "stopped") {
256
- throw new Error("Camera is already stopped");
257
- }
258
- if (this._state === "starting") {
259
- await new Promise((resolve) => this.once("started", resolve));
260
- }
261
- this._state = "stopping";
262
- this.emit("stopping");
263
- if (this.videoStream) {
264
- this.videoStream.getVideoTracks().forEach((track) => track.stop());
265
- }
266
- this.videoStream = null;
267
- this.videoElement.srcObject = null;
268
- if (this._resizeOnWindowChange) {
269
- window.removeEventListener("resize", this.notifyContainerSizeChanged);
270
- }
271
- this._state = "stopped";
272
- this.emit("stopped");
273
- }
274
- release() {
275
- this.videoContainer.removeChild(this.videoElement);
276
- SharedCameras$1._remove(this);
277
- }
278
- static async checkAvailability(testUserMedia = false) {
279
- if (!navigator.mediaDevices) {
280
- throw new Error("navigator.mediaDevices not present in your browser");
281
- }
282
- if (!navigator.mediaDevices.enumerateDevices) {
283
- throw new Error("navigator.mediaDevices.enumerateDevices not present in your browser");
284
- }
285
- if (!navigator.mediaDevices.getUserMedia) {
286
- throw new Error("navigator.mediaDevices.getUserMedia not present in your browser");
287
- }
288
- const devices = await navigator.mediaDevices.enumerateDevices();
289
- if (!devices.find((device) => device.kind === "videoinput")) {
290
- throw new Error("No camera found");
291
- }
292
- if (testUserMedia) {
293
- const stream = await navigator.mediaDevices.getUserMedia({
294
- audio: false,
295
- video: { facingMode: "environment" }
296
- });
297
- stream.getVideoTracks().forEach((track) => track.stop());
298
- }
299
- }
300
- get state() {
301
- return this._state;
302
- }
303
- get hardwareVerticalFov() {
304
- return this._hardwareVerticalFov;
305
- }
306
- set hardwareVerticalFov(hardwareVerticalFov) {
307
- this._hardwareVerticalFov = hardwareVerticalFov;
308
- this._computeFov();
309
- }
310
- get currentImage() {
311
- if (this._state !== "started") {
312
- Logger.warn("Trying to access image but video is not started");
313
- return Promise.resolve(null);
314
- }
315
- const { videoWidth: width, videoHeight: height } = this.videoElement;
316
- const canvas = document.createElement("canvas");
317
- const context = canvas.getContext("2d");
318
- canvas.width = width;
319
- canvas.height = height;
320
- context == null ? void 0 : context.drawImage(this.videoElement, 0, 0, width, height);
321
- return Promise.resolve(canvas);
322
- }
323
- };
324
- __publicField(_Camera, "DEFAULT_OPTIONS", {
325
- width: 1024,
326
- height: 768,
327
- resizeOnWindowChange: false
328
- });
329
- __publicField(_Camera, "GENERIC_HARDWARE_VERTICAL_FOV", 60);
330
- let Camera = _Camera;
331
- class QrCodeScanner extends EventEmitter {
332
- constructor(camera) {
333
- super();
334
- __publicField(this, "_height");
335
- __publicField(this, "_width");
336
- __publicField(this, "_camera");
337
- __publicField(this, "_videoElement");
338
- __publicField(this, "_canvas");
339
- __publicField(this, "_ctx");
340
- __publicField(this, "_intervalTime", null);
341
- __publicField(this, "_intervalTick");
342
- __publicField(this, "_isStarted", false);
343
- this._camera = camera;
344
- this._videoElement = camera.videoElement;
345
- this._canvas = document.createElement("canvas");
346
- this._ctx = this._canvas.getContext("2d");
347
- this._camera.on("started", () => this._tryStart());
348
- this._camera.on("stopped", () => this._stop());
349
- }
350
- start(intervalTime = 500) {
351
- this._intervalTime = intervalTime;
352
- if (this._isStarted) {
353
- return;
354
- }
355
- this._isStarted = true;
356
- this._tryStart();
357
- }
358
- stop() {
359
- this._isStarted = false;
360
- this._stop();
361
- }
362
- _tryStart() {
363
- if (!this._isStarted || this._camera.state !== "started") {
364
- return;
365
- }
366
- this._width = this._videoElement.videoWidth;
367
- this._height = this._videoElement.videoHeight;
368
- this._canvas.width = this._width;
369
- this._canvas.height = this._height;
370
- this._intervalTick = window.setInterval(() => this._onFrame(), this._intervalTime);
371
- }
372
- _stop() {
373
- clearInterval(this._intervalTick);
374
- }
375
- _onFrame() {
376
- if (!this._width || !this._height) {
377
- return;
378
- }
379
- this._ctx.drawImage(this._videoElement, 0, 0, this._width, this._height);
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
- });
387
- }
388
- }
389
- exports.Camera = Camera;
390
- exports.QrCodeScanner = QrCodeScanner;
391
- exports.SharedCameras = SharedCameras$1;
392
- exports.base64ToCanvas = base64ToCanvas;
393
- exports.calcDisplayedFov = calcDisplayedFov;
394
- exports.canvasToBase64 = canvasToBase64;
395
- exports.convertFov = convertFov;
396
- exports.convertToGrayscale = convertToGrayscale;
397
- exports.createCameraCalibrationFromWidthHeightFov = createCameraCalibrationFromWidthHeightFov;
398
- exports.htmlImageToCanvas = htmlImageToCanvas;
399
- exports.reduceImageSize = reduceImageSize;
1
+ "use strict";var t=Object.defineProperty,e=(e,i,s)=>(((e,i,s)=>{i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[i]=s})(e,"symbol"!=typeof i?i+"":i,s),s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("events"),s=require("@wemap/logger"),a=require("qr-scanner");const n=new class extends i{constructor(){super(...arguments),e(this,"_list",[])}_add(t,e){const i={camera:t,container:e};this._list.push(i),this.emit("added",i)}_remove(t){this._list=this._list.filter((({camera:e})=>e!==t)),this.emit("removed",{camera:t})}get list(){return this._list}getCameraByContainer(t){var e;return(null==(e=this._list.find((e=>e.container===t)))?void 0:e.camera)||null}};function r(t,e){return 2*Math.atan(Math.tan(t/180*Math.PI/2)*e)*180/Math.PI}function o(t,e,i){const s=e.videoWidth,a=e.videoHeight;if(!s||!a)return null;const n=s/a,o=t.offsetWidth/t.offsetHeight;let h=i,d=r(h,n);return o<n?d=r(h,o):h=r(d,1/o),{vertical:h,horizontal:d}}const h=class t extends i{constructor(i,s={}){super(),e(this,"_userMediaConstraints"),e(this,"videoStream",null),e(this,"videoContainer"),e(this,"videoElement"),e(this,"fov",null),e(this,"_state","stopped"),e(this,"_hardwareVerticalFov",t.GENERIC_HARDWARE_VERTICAL_FOV),e(this,"_resizeOnWindowChange"),e(this,"notifyContainerSizeChanged",(()=>{this._resizeElement(),this._computeFov()})),e(this,"_resizeElement",(()=>{if(!this.videoElement)throw new Error("No video element found");const t=this.videoElement.videoWidth,e=this.videoElement.videoHeight,i=this.videoContainer.offsetWidth,s=this.videoContainer.offsetHeight,a=t/e;if(i/s<a){const t=a*s;this.videoElement.style.width=t+"px",this.videoElement.style.marginLeft=-(t-i)/2+"px",this.videoElement.style.height=s+"px",this.videoElement.style.marginTop="0px"}else{const t=1/(a/i);this.videoElement.style.height=t+"px",this.videoElement.style.marginTop=-(t-s)/2+"px",this.videoElement.style.width=i+"px",this.videoElement.style.marginLeft="0px"}})),e(this,"_computeFov",(()=>{["stopping","stopped"].includes(this.state)||(this.fov=o(this.videoContainer,this.videoElement,this._hardwareVerticalFov),this.emit("fov.changed",this.fov))})),s=Object.assign({},t.DEFAULT_OPTIONS,s),n._add(this,i),this.videoContainer=i,this._resizeOnWindowChange=s.resizeOnWindowChange,this._userMediaConstraints={audio:!1,video:{facingMode:"environment",width:{ideal:s.width},height:{ideal:s.height},resizeMode:"none"}},this.videoContainer.style.overflow="hidden",this.videoElement=document.createElement("video"),this.videoElement.setAttribute("id","wemap-camera"),this.videoElement.setAttribute("preload","none"),this.videoElement.setAttribute("muted",""),this.videoElement.setAttribute("playsinline",""),this.videoContainer.appendChild(this.videoElement)}async start(e){if("started"===this._state||"starting"===this._state)throw new Error("Camera is already started");"stopping"===this._state&&await new Promise((t=>this.once("stopped",t))),this._state="starting",this.emit("starting"),await t.checkAvailability(),"object"==typeof e&&Object.assign(this._userMediaConstraints.video,this._userMediaConstraints.video,e);const i=await navigator.mediaDevices.getUserMedia(this._userMediaConstraints);this.videoElement.oncanplaythrough=()=>this.videoElement.play();const s=new Promise((t=>this.videoElement.onloadedmetadata=t));this.videoElement.srcObject=this.videoStream=i,await s,this._resizeElement(),this._computeFov(),this._resizeOnWindowChange&&window.addEventListener("resize",this.notifyContainerSizeChanged),this._state="started";const a={videoElement:this.videoElement,stream:i};return this.emit("started",a),a}async stop(){if("stopped"===this._state)throw new Error("Camera is already stopped");"starting"===this._state&&await new Promise((t=>this.once("started",t))),this._state="stopping",this.emit("stopping"),this.videoStream&&this.videoStream.getVideoTracks().forEach((t=>t.stop())),this.videoStream=null,this.videoElement.srcObject=null,this._resizeOnWindowChange&&window.removeEventListener("resize",this.notifyContainerSizeChanged),this._state="stopped",this.emit("stopped")}release(){this.videoContainer.removeChild(this.videoElement),n._remove(this)}static async checkAvailability(t=!1){if(!navigator.mediaDevices)throw new Error("navigator.mediaDevices not present in your browser");if(!navigator.mediaDevices.enumerateDevices)throw new Error("navigator.mediaDevices.enumerateDevices not present in your browser");if(!navigator.mediaDevices.getUserMedia)throw new Error("navigator.mediaDevices.getUserMedia not present in your browser");if(!(await navigator.mediaDevices.enumerateDevices()).find((t=>"videoinput"===t.kind)))throw new Error("No camera found");if(t){(await navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:"environment"}})).getVideoTracks().forEach((t=>t.stop()))}}get state(){return this._state}get hardwareVerticalFov(){return this._hardwareVerticalFov}set hardwareVerticalFov(t){this._hardwareVerticalFov=t,this._computeFov()}get currentImage(){if("started"!==this._state)return s.warn("Trying to access image but video is not started"),Promise.resolve(null);const{videoWidth:t,videoHeight:e}=this.videoElement,i=document.createElement("canvas"),a=i.getContext("2d");return i.width=t,i.height=e,null==a||a.drawImage(this.videoElement,0,0,t,e),Promise.resolve(i)}};e(h,"DEFAULT_OPTIONS",{width:1024,height:768,resizeOnWindowChange:!1}),e(h,"GENERIC_HARDWARE_VERTICAL_FOV",60);let d=h;exports.Camera=d,exports.QrCodeScanner=class extends i{constructor(t){super(),e(this,"_height"),e(this,"_width"),e(this,"_camera"),e(this,"_videoElement"),e(this,"_canvas"),e(this,"_ctx"),e(this,"_intervalTime",null),e(this,"_intervalTick"),e(this,"_isStarted",!1),this._camera=t,this._videoElement=t.videoElement,this._canvas=document.createElement("canvas"),this._ctx=this._canvas.getContext("2d"),this._camera.on("started",(()=>this._tryStart())),this._camera.on("stopped",(()=>this._stop()))}start(t=500){this._intervalTime=t,this._isStarted||(this._isStarted=!0,this._tryStart())}stop(){this._isStarted=!1,this._stop()}_tryStart(){this._isStarted&&"started"===this._camera.state&&(this._width=this._videoElement.videoWidth,this._height=this._videoElement.videoHeight,this._canvas.width=this._width,this._canvas.height=this._height,this._intervalTick=window.setInterval((()=>this._onFrame()),this._intervalTime))}_stop(){clearInterval(this._intervalTick)}_onFrame(){if(!this._width||!this._height)return;this._ctx.drawImage(this._videoElement,0,0,this._width,this._height);a.scanImage(this._canvas,{returnDetailedScanResult:!0}).then((t=>{this.emit("scan",t.data)})).catch((t=>{}))}},exports.SharedCameras=n,exports.base64ToCanvas=function(t,e="image/png"){var i;if("image/png"!==e)throw new Error("Only image/png is supported");const s=document.createElement("canvas"),a=new Image;return a.src="data:image/png;base64,"+t,s.width=a.width,s.height=a.height,null==(i=s.getContext("2d"))||i.drawImage(a,0,0),s},exports.calcDisplayedFov=o,exports.canvasToBase64=function(t,e="image/png",i=null){return t.toDataURL(e,i).substring(("data:"+e+";base64,").length)},exports.convertFov=r,exports.convertToGrayscale=function(t){const e=t.getContext("2d");if(!e)return;const i=e.getImageData(0,0,t.width,t.height),s=i.data;for(let a=0;a<s.length;a+=4){const t=(s[a]+s[a+1]+s[a+2])/3;s[a]=t,s[a+1]=t,s[a+2]=t}e.putImageData(i,0,0)},exports.createCameraCalibrationFromWidthHeightFov=function(t,e,i){const s=2*Math.atan(e*Math.tan(i/2)/t);return{intrinsic:[t/(2*Math.tan(.5*i)),0,t/2,0,e/(2*Math.tan(.5*s)),e/2,0,0,1],distortions:[0,0,0,0,0]}},exports.htmlImageToCanvas=function(t){const e=document.createElement("canvas");e.width=t.width,e.height=t.height;const i=e.getContext("2d");return null==i||i.scale(e.width/t.width,e.height/t.height),null==i||i.drawImage(t,0,0),e},exports.reduceImageSize=function(t,e,i){let s=t.width,a=t.height;if(void 0!==i)s>e&&(a*=e/s,s=e),a>i&&(s*=i/a,a=i);else{const i=t.width/t.height;t.width>t.height?t.width>e&&(s=e,a=e/i):t.height>e&&(a=e,s=e*i)}if(s===t.width&&a===t.height)return t;const n=document.createElement("canvas"),r=n.getContext("2d");return n.width=s,n.height=a,null==r||r.drawImage(t,0,0,n.width,n.height),n};
400
2
  //# sourceMappingURL=index.js.map
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 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":";;;;;;;;;;;AAWA,MAAM,sBAAsB,aAAa;AAAA,EAAzC;AAAA;AAEI,iCAA2B,CAAA;AAAA;AAAA,EAE3B,KAAK,QAAgB,WAAwB;AACzC,UAAM,MAAM;AAAA,MACR;AAAA,MACA;AAAA,IAAA;AAEC,SAAA,MAAM,KAAK,GAAG;AACd,SAAA,KAAK,SAAS,GAAG;AAAA,EAC1B;AAAA,EAEA,QAAQ,QAAgB;AACf,SAAA,QAAQ,KAAK,MAAM,OAAO,CAAC,EAAE,QAAQ,QAAA,MAAc,YAAY,MAAM;AAC1E,SAAK,KAAK,WAAW,EAAE,OAAQ,CAAA;AAAA,EACnC;AAAA,EAEA,IAAI,OAAO;AACP,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,qBAAqB,WAAwB;;AAClC,aAAA,UAAK,MAAM,KAAK,CAAA,QAAO,IAAI,cAAc,SAAS,MAAlD,mBAAqD,WAAU;AAAA,EAC1E;AACJ;AAEA,MAAe,kBAAA,IAAI,cAAc;AC9BjB,SAAA,WAAW,OAAe,aAAqB;AAC3D,SAAO,IAAI,KAAK,KAAK,KAAK,IAAK,QAAQ,MAAM,KAAK,KAAM,CAAC,IAAI,WAAW,IAAI,MAAM,KAAK;AAC3F;AAEgB,SAAA,iBACZ,gBACA,cACA,qBACF;AAEE,QAAM,cAAc,aAAa;AACjC,QAAM,eAAe,aAAa;AAE9B,MAAA,CAAC,eAAe,CAAC,cAAc;AACxB,WAAA;AAAA,EACX;AAEA,QAAM,iBAAiB,eAAe;AACtC,QAAM,kBAAkB,eAAe;AAEvC,QAAM,eAAe,cAAc;AACnC,QAAM,eAAe,iBAAiB;AAEtC,MAAI,OAAO;AACP,MAAA,OAAO,WAAW,MAAM,YAAY;AAExC,MAAI,eAAe,cAAc;AACtB,WAAA,WAAW,MAAM,YAAY;AAAA,EAAA,OACjC;AACI,WAAA,WAAW,MAAM,IAAI,YAAY;AAAA,EAC5C;AAEO,SAAA;AAAA,IACH,UAAU;AAAA,IACV,YAAY;AAAA,EAAA;AAEpB;AAKO,SAAS,eAAe,QAA2B,OAAO,aAAa,UAAe,MAAM;AACxF,SAAA,OACF,UAAU,MAAM,OAAO,EACvB,WAAW,UAAU,OAAO,YAAY,MAAM;AACvD;AAEgB,SAAA,eAAe,QAAgB,OAAO,aAAa;;AAC/D,MAAI,SAAS,aAAa;AAChB,UAAA,IAAI,MAAM,6BAA6B;AAAA,EACjD;AACM,QAAA,SAAS,SAAS,cAAc,QAAQ;AACxC,QAAA,QAAQ,IAAI;AAClB,QAAM,MAAM,2BAA2B;AACvC,SAAO,QAAQ,MAAM;AACrB,SAAO,SAAS,MAAM;AACtB,eAAO,WAAW,IAAI,MAAtB,mBAAyB,UAAU,OAAO,GAAG;AACtC,SAAA;AACX;AAWgB,SAAA,0CACZ,OACA,QACA,YACW;AAEL,QAAA,cAAc,IAAI,KAAK,KAAK,SAAS,KAAK,IAAI,aAAa,CAAC,IAAI,KAAK;AAE3E,QAAM,KAAK,SAAS,IAAI,KAAK,IAAI,MAAM,UAAU;AACjD,QAAM,KAAK,UAAU,IAAI,KAAK,IAAI,MAAM,WAAW;AACnD,QAAM,KAAK,QAAQ;AACnB,QAAM,KAAK,SAAS;AAEb,SAAA;AAAA,IACH,WAAW,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,CAAC;AAAA,IACzC,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;AAAA,EAAA;AAEnC;AAKO,SAAS,mBAAmB,aAAgC;AACzD,QAAA,MAAM,YAAY,WAAW,IAAI;AACvC,MAAI,CAAC;AAAK;AAEJ,QAAA,UAAU,IAAI,aAAa,GAAG,GAAG,YAAY,OAAO,YAAY,MAAM;AAC5E,QAAM,SAAS,QAAQ;AACvB,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;AAEjC,UAAA,aAAa,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,KAAK;AAChE,WAAO,CAAC,IAAI;AACL,WAAA,IAAI,CAAC,IAAI;AACT,WAAA,IAAI,CAAC,IAAI;AAAA,EACpB;AACI,MAAA,aAAa,SAAS,GAAG,CAAC;AAClC;AAKgB,SAAA,gBACZ,aACA,UACA,WACF;AAEE,MAAI,WAAW,YAAY;AAC3B,MAAI,YAAY,YAAY;AAExB,MAAA,OAAO,cAAc,aAAa;AAClC,QAAI,WAAW,UAAU;AACrB,mBAAa,WAAW;AACb,iBAAA;AAAA,IACf;AAEA,QAAI,YAAY,WAAW;AACvB,kBAAY,YAAY;AACZ,kBAAA;AAAA,IAChB;AAAA,EAAA,OACG;AACG,UAAA,cAAc,YAAY,QAAQ,YAAY;AAChD,QAAA,YAAY,QAAQ,YAAY,QAAQ;AACpC,UAAA,YAAY,QAAQ,UAAU;AACnB,mBAAA;AACX,oBAAY,WAAW;AAAA,MAC3B;AAAA,IAAA,WACO,YAAY,SAAS,UAAU;AAC1B,kBAAA;AACZ,iBAAW,WAAW;AAAA,IAC1B;AAAA,EACJ;AAGA,MAAI,aAAa,YAAY,SACtB,cAAc,YAAY,QAAQ;AAC9B,WAAA;AAAA,EACX;AAEM,QAAA,YAAY,SAAS,cAAc,QAAQ;AAC3C,QAAA,aAAa,UAAU,WAAW,IAAI;AAE5C,YAAU,QAAQ;AAClB,YAAU,SAAS;AACnB,2CAAY,UAAU,aAAa,GAAG,GAAG,UAAU,OAAO,UAAU;AAE7D,SAAA;AACX;AAEO,SAAS,kBAAkB,OAAyB;AACjD,QAAA,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ,MAAM;AACrB,SAAO,SAAS,MAAM;AAChB,QAAA,UAAU,OAAO,WAAW,IAAI;AAC7B,qCAAA,MAAM,OAAO,QAAQ,MAAM,OAAO,OAAO,SAAS,MAAM;AACxD,qCAAA,UAAU,OAAO,GAAG;AACtB,SAAA;AACX;AC5IA,MAAM,UAAN,MAAM,gBAAe,aAAa;AAAA,EAuB9B,YAAY,WAAwB,UAAyB,IAAI;AACvD;AAdV;AACA,uCAAkC;AAElC;AACA;AAEA,+BAAqD;AAErD,kCAAsB;AAEtB,gDAAuB,QAAO;AAC9B;AAsJA,sDAA6B,MAAM;AAC/B,WAAK,eAAe;AACpB,WAAK,YAAY;AAAA,IAAA;AAGrB,0CAAiB,MAAM;AAEf,UAAA,CAAC,KAAK,cAAc;AACd,cAAA,IAAI,MAAM,wBAAwB;AAAA,MAC5C;AAEM,YAAA,cAAc,KAAK,aAAa;AAChC,YAAA,eAAe,KAAK,aAAa;AAEjC,YAAA,iBAAiB,KAAK,eAAe;AACrC,YAAA,kBAAkB,KAAK,eAAe;AAE5C,YAAM,eAAe,cAAc;AACnC,YAAM,eAAe,iBAAiB;AAEtC,UAAI,eAAe,cAAc;AAE7B,cAAM,WAAW,eAAe;AAC3B,aAAA,aAAa,MAAM,QAAQ,WAAW;AAC3C,aAAK,aAAa,MAAM,aAAa,EAAE,WAAW,kBAAkB,IAAI;AAEnE,aAAA,aAAa,MAAM,SAAS,kBAAkB;AAC9C,aAAA,aAAa,MAAM,YAAY;AAAA,MAAA,OAEjC;AAEG,cAAA,YAAY,KAAK,eAAe;AACjC,aAAA,aAAa,MAAM,SAAS,YAAY;AAC7C,aAAK,aAAa,MAAM,YAAY,EAAE,YAAY,mBAAmB,IAAI;AAEpE,aAAA,aAAa,MAAM,QAAQ,iBAAiB;AAC5C,aAAA,aAAa,MAAM,aAAa;AAAA,MACzC;AAAA,IAAA;AAIJ,uCAAc,MAAM;AAEhB,UAAI,CAAC,YAAY,SAAS,EAAE,SAAS,KAAK,KAAK,GAAG;AAC9C;AAAA,MACJ;AAEA,WAAK,MAAM;AAAA,QACP,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MAAA;AAGJ,WAAA,KAAK,eAAe,KAAK,GAAG;AAAA,IAAA;AAtMjC,cAAU,OAAO,OAAO,CAAA,GAAI,QAAO,iBAAiB,OAAO;AAE7CA,oBAAA,KAAK,MAAM,SAAS;AAElC,SAAK,iBAAiB;AAEtB,SAAK,wBAAwB,QAAQ;AACrC,SAAK,wBAAwB;AAAA,MACzB,OAAO;AAAA,MACP,OAAO;AAAA,QACH,YAAY;AAAA,QACZ,OAAO,EAAE,OAAO,QAAQ,MAAM;AAAA,QAC9B,QAAQ,EAAE,OAAO,QAAQ,OAAO;AAAA,QAChC,YAAY;AAAA,MAChB;AAAA;AAAA,IAAA;AAGC,SAAA,eAAe,MAAM,WAAW;AAEhC,SAAA,eAAe,SAAS,cAAc,OAAO;AAC7C,SAAA,aAAa,aAAa,MAAM,cAAc;AAC9C,SAAA,aAAa,aAAa,WAAW,MAAM;AAC3C,SAAA,aAAa,aAAa,SAAS,EAAE;AACrC,SAAA,aAAa,aAAa,eAAe,EAAE;AAC3C,SAAA,eAAe,YAAY,KAAK,YAAY;AAAA,EACrD;AAAA,EAEA,MAAM,MAAM,uBAA+C;AAEvD,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY;AACnD,YAAA,IAAI,MAAM,2BAA2B;AAAA,IAC/C;AAEI,QAAA,KAAK,WAAW,YAAY;AAC5B,YAAM,IAAI,QAAQ,CAAA,YAAW,KAAK,KAAK,WAAW,OAAO,CAAC;AAAA,IAC9D;AAEA,SAAK,SAAS;AACd,SAAK,KAAK,UAAU;AAEpB,UAAM,QAAO;AAET,QAAA,OAAO,0BAA0B,UAAU;AACpC,aAAA;AAAA,QAAO,KAAK,sBAAsB;AAAA,QACrC,KAAK,sBAAsB;AAAA,QAAgC;AAAA,MAAA;AAAA,IACnE;AAEA,UAAM,SAAS,MAAM,UAAU,aAAa,aAAa,KAAK,qBAAqB;AAGnF,SAAK,aAAa,mBAAmB,MAAM,KAAK,aAAa;AAC7D,UAAM,aAAa,IAAI,QAAQ,aAAY,KAAK,aAAa,mBAAmB,OAAQ;AAEnF,SAAA,aAAa,YAAY,KAAK,cAAc;AAC3C,UAAA;AAEN,SAAK,eAAe;AACpB,SAAK,YAAY;AAEjB,QAAI,KAAK,uBAAuB;AACrB,aAAA,iBAAiB,UAAU,KAAK,0BAA0B;AAAA,IACrE;AAEA,SAAK,SAAS;AACd,UAAM,SAAS;AAAA,MACX,cAAc,KAAK;AAAA,MACnB;AAAA,IAAA;AAGC,SAAA,KAAK,WAAW,MAAM;AACpB,WAAA;AAAA,EACX;AAAA,EAEA,MAAM,OAAO;AAEL,QAAA,KAAK,WAAW,WAAW;AACrB,YAAA,IAAI,MAAM,2BAA2B;AAAA,IAC/C;AAEI,QAAA,KAAK,WAAW,YAAY;AAC5B,YAAM,IAAI,QAAQ,CAAA,YAAW,KAAK,KAAK,WAAW,OAAO,CAAC;AAAA,IAC9D;AAEA,SAAK,SAAS;AACd,SAAK,KAAK,UAAU;AAEpB,QAAI,KAAK,aAAa;AAClB,WAAK,YAAY,iBAAiB,QAAQ,CAAS,UAAA,MAAM,MAAM;AAAA,IACnE;AACA,SAAK,cAAc;AACnB,SAAK,aAAa,YAAY;AAE9B,QAAI,KAAK,uBAAuB;AACrB,aAAA,oBAAoB,UAAU,KAAK,0BAA0B;AAAA,IACxE;AAEA,SAAK,SAAS;AACd,SAAK,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,UAAU;AACD,SAAA,eAAe,YAAY,KAAK,YAAY;AACjDA,oBAAc,QAAQ,IAAI;AAAA,EAC9B;AAAA,EAEA,aAAa,kBAAkB,gBAAgB,OAAO;AAE9C,QAAA,CAAC,UAAU,cAAc;AACnB,YAAA,IAAI,MAAM,oDAAoD;AAAA,IACxE;AAEI,QAAA,CAAC,UAAU,aAAa,kBAAkB;AACpC,YAAA,IAAI,MAAM,qEAAqE;AAAA,IACzF;AAEI,QAAA,CAAC,UAAU,aAAa,cAAc;AAChC,YAAA,IAAI,MAAM,iEAAiE;AAAA,IACrF;AAEA,UAAM,UAAU,MAAM,UAAU,aAAa,iBAAiB;AAC9D,QAAI,CAAC,QAAQ,KAAK,YAAU,OAAO,SAAS,YAAY,GAAG;AACjD,YAAA,IAAI,MAAM,iBAAiB;AAAA,IACrC;AAEA,QAAI,eAAe;AACf,YAAM,SAAS,MAAM,UAAU,aAAa,aAAa;AAAA,QACrD,OAAO;AAAA,QAAO,OAAO,EAAE,YAAY,cAAc;AAAA,MAAA,CACpD;AACD,aAAO,iBAAiB,QAAQ,CAAS,UAAA,MAAM,MAAM;AAAA,IACzD;AAAA,EACJ;AAAA,EAEA,IAAI,QAAQ;AACR,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,sBAAsB;AACtB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,oBAAoB,qBAAqB;AACzC,SAAK,uBAAuB;AAC5B,SAAK,YAAY;AAAA,EACrB;AAAA,EA0DA,IAAI,eAAe;AAEX,QAAA,KAAK,WAAW,WAAW;AAC3B,aAAO,KAAK,iDAAiD;AACtD,aAAA,QAAQ,QAAQ,IAAI;AAAA,IAC/B;AAEA,UAAM,EAAE,YAAY,OAAO,aAAa,WAAW,KAAK;AAClD,UAAA,SAAS,SAAS,cAAc,QAAQ;AACxC,UAAA,UAAU,OAAO,WAAW,IAAI;AAGtC,WAAO,QAAQ;AACf,WAAO,SAAS;AAEhB,uCAAS,UAAU,KAAK,cAAc,GAAG,GAAG,OAAO;AAC5C,WAAA,QAAQ,QAAQ,MAAM;AAAA,EACjC;AACJ;AAnPI,cAFE,SAEK,mBAAkB;AAAA,EACrB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,sBAAsB;AAAA;AAG1B,cARE,SAQK,iCAAgC;AAR3C,IAAM,SAAN;ACxBA,MAAM,sBAAsB,aAAa;AAAA,EAYrC,YAAY,QAAgB;AAClB;AAZV;AACA;AACA;AACA;AACA;AACA;AACA,yCAA+B;AAC/B;AACA,sCAAa;AAMT,SAAK,UAAU;AACf,SAAK,gBAAgB,OAAO;AAGvB,SAAA,UAAU,SAAS,cAAc,QAAQ;AAC9C,SAAK,OAAO,KAAK,QAAQ,WAAW,IAAI;AAExC,SAAK,QAAQ,GAAG,WAAW,MAAM,KAAK,WAAW;AACjD,SAAK,QAAQ,GAAG,WAAW,MAAM,KAAK,OAAO;AAAA,EACjD;AAAA,EAEA,MAAM,eAAe,KAAK;AACtB,SAAK,gBAAgB;AAErB,QAAI,KAAK,YAAY;AACjB;AAAA,IACJ;AACA,SAAK,aAAa;AAElB,SAAK,UAAU;AAAA,EACnB;AAAA,EAEA,OAAO;AACH,SAAK,aAAa;AAClB,SAAK,MAAM;AAAA,EACf;AAAA,EAEA,YAAY;AAER,QAAI,CAAC,KAAK,cAAc,KAAK,QAAQ,UAAU,WAAW;AACtD;AAAA,IACJ;AAEK,SAAA,SAAS,KAAK,cAAc;AAC5B,SAAA,UAAU,KAAK,cAAc;AAC7B,SAAA,QAAQ,QAAQ,KAAK;AACrB,SAAA,QAAQ,SAAS,KAAK;AAEtB,SAAA,gBAAgB,OAAO,YAAY,MAAM,KAAK,SAAS,GAAG,KAAK,aAAuB;AAAA,EAC/F;AAAA,EAEA,QAAQ;AACJ,kBAAc,KAAK,aAAa;AAAA,EACpC;AAAA,EAEA,WAAW;AAEP,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,SAAS;AAC/B;AAAA,IACJ;AAEK,SAAA,KAAK,UAAU,KAAK,eAAe,GAAG,GAAG,KAAK,QAAQ,KAAK,OAAO;AACvE,UAAM,oBAAoB,UAAU,UAAU,KAAK,SAAS;AAAA,MACxD,0BAA0B;AAAA,IAAA,CAC7B;AAEiB,sBAAA,KAAK,CAAC,WAAW;AAC1B,WAAA,KAAK,QAAQ,OAAO,IAAI;AAAA,IAAA,CAChC,EAAE,MAAM,CAAC,MAAM;AAAA,IAAA,CAEf;AAAA,EACL;AACJ;;;;;;;;;;;;"}
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$1","EventEmitter","constructor","super","arguments","__publicField","this","_add","camera","container","obj","_list","push","emit","_remove","filter","_camera","list","getCameraByContainer","_a","find","convertFov","angle","aspectRatio","Math","atan","tan","PI","calcDisplayedFov","videoContainer","videoElement","hardwareVerticalFov","sourceWidth","videoWidth","sourceHeight","videoHeight","sourceAspect","screenAspect","offsetWidth","offsetHeight","fovV","fovH","vertical","horizontal","_Camera","options","GENERIC_HARDWARE_VERTICAL_FOV","_resizeElement","_computeFov","Error","containerWidth","containerHeight","newWidth","style","width","marginLeft","height","marginTop","newHeight","includes","state","fov","_hardwareVerticalFov","Object","assign","DEFAULT_OPTIONS","_resizeOnWindowChange","resizeOnWindowChange","_userMediaConstraints","audio","video","facingMode","ideal","resizeMode","overflow","document","createElement","setAttribute","appendChild","start","videoMediaConstraints","_state","Promise","resolve","once","checkAvailability","stream","navigator","mediaDevices","getUserMedia","oncanplaythrough","play","metadataPr","onloadedmetadata","srcObject","videoStream","window","addEventListener","notifyContainerSizeChanged","output","stop","getVideoTracks","forEach","track","removeEventListener","release","removeChild","SharedCameras","testUserMedia","enumerateDevices","device","kind","currentImage","Logger","warn","canvas","context","getContext","drawImage","Camera","_videoElement","_canvas","_ctx","on","_tryStart","_stop","intervalTime","_intervalTime","_isStarted","_width","_height","_intervalTick","setInterval","_onFrame","clearInterval","QrScanner","scanImage","returnDetailedScanResult","then","result","data","catch","e","base64","type","image","Image","src","quality","toDataURL","substring","length","imageCanvas","ctx","imgData","getImageData","pixels","i","lightness","putImageData","fovOfWidth","fovOfHeight","intrinsic","distortions","scale","maxWidth","maxHeight","newCanvas","newContext"],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { default as EventEmitter } from 'events';
2
+ type CameraOptions = {
3
+ width?: number;
4
+ height?: number;
5
+ resizeOnWindowChange?: boolean;
6
+ };
7
+ export type CameraState = 'stopped' | 'starting' | 'started' | 'stopping';
8
+ declare interface Camera {
9
+ on(event: 'starting', listener: () => void): this;
10
+ on(event: 'started', listener: (obj: ({
11
+ videoElement: HTMLVideoElement;
12
+ stream: MediaStream;
13
+ })) => void): this;
14
+ on(event: 'stopping', listener: () => void): this;
15
+ on(event: 'stopped', listener: () => void): this;
16
+ on(event: 'fov.changed', listener: (obj: ({
17
+ vertical: number;
18
+ horizontal: number;
19
+ })) => void): this;
20
+ off(event: 'starting', listener: () => void): this;
21
+ off(event: 'started', listener: (obj: ({
22
+ videoElement: HTMLVideoElement;
23
+ stream: MediaStream;
24
+ })) => void): this;
25
+ off(event: 'stopping', listener: () => void): this;
26
+ off(event: 'stopped', listener: () => void): this;
27
+ off(event: 'fov.changed', listener: (obj: ({
28
+ vertical: number;
29
+ horizontal: number;
30
+ })) => void): this;
31
+ }
32
+ declare class Camera extends EventEmitter {
33
+ static DEFAULT_OPTIONS: {
34
+ width: number;
35
+ height: number;
36
+ resizeOnWindowChange: boolean;
37
+ };
38
+ static GENERIC_HARDWARE_VERTICAL_FOV: number;
39
+ _userMediaConstraints: MediaStreamConstraints;
40
+ videoStream: MediaStream | null;
41
+ videoContainer: HTMLElement;
42
+ videoElement: HTMLVideoElement;
43
+ fov: {
44
+ horizontal: number;
45
+ vertical: number;
46
+ } | null;
47
+ _state: CameraState;
48
+ _hardwareVerticalFov: number;
49
+ _resizeOnWindowChange: boolean | undefined;
50
+ constructor(container: HTMLElement, options?: CameraOptions);
51
+ start(videoMediaConstraints?: MediaTrackConstraints): Promise<{
52
+ videoElement: HTMLVideoElement;
53
+ stream: MediaStream;
54
+ }>;
55
+ stop(): Promise<void>;
56
+ release(): void;
57
+ static checkAvailability(testUserMedia?: boolean): Promise<void>;
58
+ get state(): CameraState;
59
+ get hardwareVerticalFov(): number;
60
+ set hardwareVerticalFov(hardwareVerticalFov: number);
61
+ notifyContainerSizeChanged: () => void;
62
+ _resizeElement: () => void;
63
+ _computeFov: () => void;
64
+ get currentImage(): Promise<null> | Promise<HTMLCanvasElement>;
65
+ }
66
+ export default Camera;
@@ -0,0 +1,35 @@
1
+ export type IntrinsicParams = [number, number, number, number, number, number, number, number, number];
2
+ export type DistortionsParams = [number, number, number, number, number];
3
+ export type Calibration = {
4
+ intrinsic: IntrinsicParams;
5
+ distortions?: DistortionsParams;
6
+ };
7
+ export declare function convertFov(angle: number, aspectRatio: number): number;
8
+ export declare function calcDisplayedFov(videoContainer: HTMLElement, videoElement: HTMLVideoElement, hardwareVerticalFov: number): {
9
+ vertical: number;
10
+ horizontal: number;
11
+ } | null;
12
+ /**
13
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
14
+ */
15
+ export declare function canvasToBase64(canvas: HTMLCanvasElement, type?: string, quality?: any): string;
16
+ export declare function base64ToCanvas(base64: string, type?: string): HTMLCanvasElement;
17
+ /**
18
+ * Creates a pinhole camera from dimensions and a field of view
19
+ * Distortions are not considered
20
+ *
21
+ * @param {number} width the camera width
22
+ * @param {number} height the camera height
23
+ * @param {number} fovOfWidth the field of view along the width axis in radians
24
+ * @returns {object} the calibration matrix
25
+ */
26
+ export declare function createCameraCalibrationFromWidthHeightFov(width: number, height: number, fovOfWidth: number): Calibration;
27
+ /**
28
+ * @param {HTMLCanvasElement} imageCanvas
29
+ */
30
+ export declare function convertToGrayscale(imageCanvas: HTMLCanvasElement): void;
31
+ /**
32
+ * Reduce image size and keep aspect ratio
33
+ */
34
+ export declare function reduceImageSize(imageCanvas: HTMLCanvasElement, maxWidth: number, maxHeight?: number): HTMLCanvasElement;
35
+ export declare function htmlImageToCanvas(image: HTMLImageElement): HTMLCanvasElement;