web-wasm-barcode-reader 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # Web WASM Barcode Reader
2
+
3
+ A browser-based barcode scanner powered by [ZBar](https://zbar.sourceforge.net/) compiled to WebAssembly. Works on any browser, including Safari/iOS where the native [Barcode Detection API](https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API) isn't available.
4
+
5
+ Built with TypeScript, Vite, and a wrapper-ready `BarcodeScanner` class designed for easy integration into React, Vue, or any frontend framework.
6
+
7
+ Based on the blog post: [Using the ZBar barcode scanning suite in the browser with WebAssembly](https://barkeywolf.consulting/posts/barcode-scanner-webassembly/).
8
+
9
+ ## Features
10
+
11
+ - **Real-time camera scanning** with configurable scan region and interval
12
+ - **Rotation-based skew correction** — scans at 0°, +30°, and -30° with early exit, so tilted barcodes are detected without perfect alignment
13
+ - **Animated CSS overlay** — dark viewfinder mask, corner brackets, and a sweeping laser line (replaces the old static PNG)
14
+ - **Audible beep** on detection (base64-encoded, no external file)
15
+ - **Torch toggle** for devices that support flashlight control
16
+ - **Debug preview panel** — shows all three rotation passes side-by-side with detection highlighting
17
+ - **Framework-agnostic** — `BarcodeScanner` class with `start()`/`stop()` lifecycle, constructor has no side effects (React strict-mode safe)
18
+
19
+ ## Supported Barcode Formats
20
+
21
+ ZBar supports the following symbologies:
22
+
23
+ | 1D | 2D |
24
+ |---|---|
25
+ | EAN-13, EAN-8 | QR Code |
26
+ | UPC-A, UPC-E | |
27
+ | ISBN-10, ISBN-13 | |
28
+ | Code 128, Code 39, Code 93 | |
29
+ | Interleaved 2 of 5 (I25) | |
30
+ | DataBar | |
31
+
32
+ > **Note:** Data Matrix, PDF417, and Aztec codes are not supported by ZBar.
33
+
34
+ ## Quick Start
35
+
36
+ ```sh
37
+ npm install
38
+ npm run dev
39
+ ```
40
+
41
+ Open the URL shown by Vite (typically `http://localhost:5173`). Grant camera access and point at a barcode within the scan region.
42
+
43
+ ### Production Build
44
+
45
+ ```sh
46
+ npm run build
47
+ npm run preview
48
+ ```
49
+
50
+ The build output goes to `dist/`. Type-checking runs automatically before the Vite build.
51
+
52
+ ## Project Structure
53
+
54
+ ```
55
+ web-wasm-barcode-reader/
56
+ ├── public/
57
+ │ ├── a.out.js # Emscripten JS glue (pre-compiled)
58
+ │ └── a.out.wasm # ZBar WASM binary (pre-compiled)
59
+ ├── src/
60
+ │ ├── scanner.ts # BarcodeScanner class — core lifecycle + scan loop
61
+ │ ├── overlay.ts # Animated CSS scan overlay (mask, brackets, laser)
62
+ │ ├── main.ts # Demo entry point
63
+ │ └── types/
64
+ │ └── emscripten.d.ts # TypeScript declarations for the Emscripten Module
65
+ ├── index.html # Demo page
66
+ ├── index2.html # Standalone OCR/MRZ experiment (Tesseract.js)
67
+ ├── scan.c # C source — ZBar image scanning + WASM buffer management
68
+ ├── library.js # Emscripten JS library — bridges WASM results to JS
69
+ ├── package.json
70
+ ├── tsconfig.json # strict: true, noImplicitAny: true
71
+ └── vite.config.ts
72
+ ```
73
+
74
+ ## Architecture
75
+
76
+ ### How a Scan Works
77
+
78
+ ```
79
+ Camera frame
80
+
81
+ ├─ drawImage() ──► Offscreen canvas (crop scan region from video)
82
+ │ │
83
+ │ ├─ Rotate 0° ──► grayscale ──► WASM scan_image()
84
+ │ ├─ Rotate +30° ──► grayscale ──► WASM scan_image() (only if 0° missed)
85
+ │ └─ Rotate -30° ──► grayscale ──► WASM scan_image() (only if ±30° missed)
86
+
87
+ └─ On detection: polygon overlay + beep + onDetect callback
88
+ ```
89
+
90
+ ### WASM Bridge
91
+
92
+ The scanner communicates with ZBar through three C functions exposed via Emscripten:
93
+
94
+ | C function | Purpose |
95
+ |---|---|
96
+ | `create_buffer(w, h)` | `malloc` a buffer on the WASM heap for image data |
97
+ | `scan_image(ptr, w, h)` | Run ZBar on grayscale pixels at `ptr`. Calls `js_output_result` on hit. |
98
+ | `destroy_buffer(ptr)` | `free` a buffer (unused — ZBar frees it internally via `zbar_image_free_data`) |
99
+
100
+ `library.js` defines `js_output_result`, which reads symbol name, data, and polygon coordinates from the WASM heap and forwards them to `Module.processResult` — a callback set by the `BarcodeScanner` class.
101
+
102
+ > **Important:** `scan_image` registers the buffer with `zbar_image_free_data` as the cleanup handler, so ZBar frees the buffer when the image is destroyed. A fresh buffer must be allocated for every `scan_image` call — reusing a pointer is use-after-free.
103
+
104
+ ## API
105
+
106
+ ### `BarcodeScanner`
107
+
108
+ ```typescript
109
+ import { BarcodeScanner } from './scanner';
110
+
111
+ const scanner = new BarcodeScanner({
112
+ container: document.getElementById('scanner-mount')!,
113
+ onDetect: (result) => {
114
+ console.log(result.symbol, result.data);
115
+ },
116
+ });
117
+
118
+ await scanner.start();
119
+ // later...
120
+ scanner.stop();
121
+ ```
122
+
123
+ ### `ScannerOptions`
124
+
125
+ | Option | Type | Default | Description |
126
+ |---|---|---|---|
127
+ | `container` | `HTMLElement` | *required* | Element to mount the scanner into |
128
+ | `onDetect` | `(result: ScanResult) => void` | *required* | Called on each barcode detection |
129
+ | `onError` | `(error: Error) => void` | `console.error` | Called on unrecoverable errors |
130
+ | `scanInterval` | `number` | `150` | Milliseconds between scan attempts |
131
+ | `beepOnDetect` | `boolean` | `true` | Play an audible beep on detection |
132
+ | `facingMode` | `'environment' \| 'user'` | `'environment'` | Camera facing mode |
133
+ | `scanRegion` | `{ width: number; height: number }` | `{ width: 0.702, height: 0.242 }` | Scan region as fraction of container |
134
+ | `previewCanvas` | `HTMLCanvasElement` | `undefined` | Optional canvas for rotation debug preview |
135
+
136
+ ### `ScanResult`
137
+
138
+ | Field | Type | Description |
139
+ |---|---|---|
140
+ | `symbol` | `string` | Barcode symbology (e.g. `"EAN-13"`, `"QR-Code"`) |
141
+ | `data` | `string` | Decoded barcode content |
142
+ | `polygon` | `number[]` | Flat `[x1, y1, x2, y2, ...]` bounding polygon in container coordinates |
143
+
144
+ ### Methods
145
+
146
+ | Method | Description |
147
+ |---|---|
148
+ | `start(): Promise<void>` | Initialize camera, WASM, and start scanning |
149
+ | `stop(): void` | Stop scanning, release camera, remove DOM elements |
150
+ | `toggleTorch(): Promise<boolean>` | Toggle flashlight, returns new state |
151
+ | `isRunning: boolean` | Whether the scanner is currently active |
152
+
153
+ ## Framework Integration
154
+
155
+ The `BarcodeScanner` constructor has **no side effects** — it only stores configuration. This makes it safe to use with React strict mode, Vue lifecycle hooks, etc.
156
+
157
+ ### React Example
158
+
159
+ ```tsx
160
+ import { useEffect, useRef } from 'react';
161
+ import { BarcodeScanner } from './scanner';
162
+
163
+ function Scanner({ onScan }: { onScan: (data: string) => void }) {
164
+ const mountRef = useRef<HTMLDivElement>(null);
165
+
166
+ useEffect(() => {
167
+ const scanner = new BarcodeScanner({
168
+ container: mountRef.current!,
169
+ onDetect: (result) => onScan(result.data),
170
+ });
171
+ scanner.start();
172
+ return () => scanner.stop();
173
+ }, [onScan]);
174
+
175
+ return <div ref={mountRef} style={{ width: 400, height: 400 }} />;
176
+ }
177
+ ```
178
+
179
+ ## Recompiling the WASM Module
180
+
181
+ The `public/a.out.js` and `public/a.out.wasm` files are pre-compiled. To rebuild them, you need [Emscripten](https://emscripten.org/) and ZBar installed:
182
+
183
+ ```sh
184
+ emcc scan.c \
185
+ -s WASM=1 \
186
+ -s EXPORTED_FUNCTIONS='["_scan_image", "_create_buffer", "_destroy_buffer"]' \
187
+ -s EXPORTED_RUNTIME_METHODS='["cwrap", "UTF8ToString"]' \
188
+ --js-library library.js \
189
+ -lzbar \
190
+ -o public/a.out.js
191
+ ```
192
+
193
+ The exact flags may vary depending on your Emscripten version and ZBar installation path.
194
+
195
+ ## Scripts
196
+
197
+ | Script | Command | Description |
198
+ |---|---|---|
199
+ | `dev` | `npm run dev` | Start Vite dev server with HMR |
200
+ | `build` | `npm run build` | Type-check + production build to `dist/` |
201
+ | `preview` | `npm run preview` | Serve the production build locally |
202
+
203
+ ## Browser Requirements
204
+
205
+ - **getUserMedia** (camera access) — all modern browsers
206
+ - **WebAssembly** — all modern browsers
207
+ - **OffscreenCanvas** — Chrome 69+, Firefox 105+, Safari 16.4+
208
+ - HTTPS or localhost (required for camera access)
209
+
210
+ ## License
211
+
212
+ See the [ZBar license](http://zbar.sourceforge.net/about.html) for the scanning library. The JavaScript/TypeScript wrapper code in this repository is unlicensed.
@@ -0,0 +1,4 @@
1
+ export { BarcodeScanner } from './scanner';
2
+ export type { ScannerOptions, ScanResult } from './scanner';
3
+ export { createOverlay, removeOverlay } from './overlay';
4
+ export type { ScanRegion } from './overlay';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Programmatic scan overlay — replaces the static barcodelayout.png.
3
+ *
4
+ * Creates a dark viewfinder mask with a transparent scan region,
5
+ * L-shaped corner brackets, and an animated sweeping laser line.
6
+ * All styling is injected via a single <style> tag (deduplicated).
7
+ */
8
+ export interface ScanRegion {
9
+ /** Fraction of container width (0–1). */
10
+ widthRatio: number;
11
+ /** Fraction of container height (0–1). */
12
+ heightRatio: number;
13
+ }
14
+ /**
15
+ * Create the overlay DOM tree inside `container`.
16
+ *
17
+ * Returns the overlay root element so the caller can remove it later.
18
+ * The scan region is positioned relative to the container using the
19
+ * provided width/height ratios (e.g. 0.702 × 0.242 matches the original
20
+ * barcodelayout.png proportions).
21
+ */
22
+ export declare function createOverlay(container: HTMLElement, scanRegion: ScanRegion): HTMLElement;
23
+ /** Remove the overlay and clean up. */
24
+ export declare function removeOverlay(overlayRoot: HTMLElement): void;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * BarcodeScanner — wrapper-ready barcode scanning class.
3
+ *
4
+ * Manages the full lifecycle: DOM setup, camera stream, WASM loading,
5
+ * scan loop with rotation-based skew correction, and cleanup.
6
+ * Designed for easy integration into React/Vue/vanilla wrappers.
7
+ *
8
+ * Constructor is side-effect-free (React strict-mode safe).
9
+ * Call start() to activate, stop() to tear down.
10
+ */
11
+ export interface ScannerOptions {
12
+ /** Container element the scanner will mount into. */
13
+ container: HTMLElement;
14
+ /** Called on every successful barcode detection. */
15
+ onDetect: (result: ScanResult) => void;
16
+ /** Called on unrecoverable errors (camera denied, WASM load failure, etc.). */
17
+ onError?: (error: Error) => void;
18
+ /** Milliseconds between scan attempts. Default: 150. */
19
+ scanInterval?: number;
20
+ /** Play an audible beep on detection. Default: true. */
21
+ beepOnDetect?: boolean;
22
+ /** Camera facing mode. Default: 'environment'. */
23
+ facingMode?: 'environment' | 'user';
24
+ /**
25
+ * Scan region as a fraction of the container dimensions.
26
+ * Default: { width: 0.702, height: 0.242 } — matches the original barcode layout.
27
+ */
28
+ scanRegion?: {
29
+ width: number;
30
+ height: number;
31
+ };
32
+ /**
33
+ * Optional canvas to render a live preview of what each rotation pass
34
+ * sees. Shows all three angles (0°, +30°, -30°) stacked vertically.
35
+ * The detected angle gets a green highlight.
36
+ */
37
+ previewCanvas?: HTMLCanvasElement;
38
+ }
39
+ export interface ScanResult {
40
+ /** Barcode symbology (e.g. "EAN-13", "QR-Code"). */
41
+ symbol: string;
42
+ /** Decoded barcode data string. */
43
+ data: string;
44
+ /** Flat polygon coordinates [x1,y1,x2,y2,...] in container-relative pixels. */
45
+ polygon: number[];
46
+ }
47
+ export declare class BarcodeScanner {
48
+ private readonly container;
49
+ private readonly onDetect;
50
+ private readonly onError;
51
+ private readonly scanInterval;
52
+ private readonly beepOnDetect;
53
+ private readonly facingMode;
54
+ private readonly regionWidth;
55
+ private readonly regionHeight;
56
+ private _isRunning;
57
+ private video;
58
+ private polyCanvas;
59
+ private polyCtx;
60
+ private offscreen;
61
+ private offCtx;
62
+ private overlayRoot;
63
+ private stream;
64
+ private timerId;
65
+ private wasmApi;
66
+ private beepAudio;
67
+ private previewCanvas;
68
+ private previewCtx;
69
+ private cameraWidth;
70
+ private cameraHeight;
71
+ private barcodeWidth;
72
+ private barcodeHeight;
73
+ private barcodeOffsetX;
74
+ private barcodeOffsetY;
75
+ /** Tracks whether we detected a barcode in the current scan tick. */
76
+ private detectedThisTick;
77
+ get isRunning(): boolean;
78
+ constructor(options: ScannerOptions);
79
+ start(): Promise<void>;
80
+ stop(): void;
81
+ /**
82
+ * Toggle the device torch (flashlight). Returns the new torch state.
83
+ * Only works on devices that support the torch constraint (most Android phones).
84
+ */
85
+ toggleTorch(): Promise<boolean>;
86
+ private setupDOM;
87
+ private teardownDOM;
88
+ /**
89
+ * Wait for the Emscripten Module to finish initializing.
90
+ * The script is loaded via a classic <script> tag in index.html,
91
+ * so by the time start() runs it may already be ready.
92
+ */
93
+ private loadWasm;
94
+ private startCamera;
95
+ private stopCamera;
96
+ /**
97
+ * Wire up Module.processResult so the WASM-side library.js callback
98
+ * routes detected barcodes back into our class.
99
+ */
100
+ private setupResultHandler;
101
+ private startScanLoop;
102
+ private stopScanLoop;
103
+ /**
104
+ * One scan tick: capture the barcode region, convert to grayscale,
105
+ * and run through WASM. Uses rotation-based skew correction —
106
+ * tries 0° first, then ±30° only if no barcode was found.
107
+ */
108
+ private scanTick;
109
+ /**
110
+ * Convert RGBA pixel data to grayscale using the BT.601 luma formula.
111
+ * Uses integer arithmetic for speed (same formula as the original index.js).
112
+ */
113
+ private toGrayscale;
114
+ /** Draw a polygon outline on the overlay canvas. */
115
+ private drawPoly;
116
+ }
@@ -0,0 +1,313 @@
1
+ const g = "barcode-scanner-overlay-styles", y = "rgba(0, 0, 0, 0.55)", x = "#00ff88";
2
+ const f = "#00ff88";
3
+ function E() {
4
+ if (document.getElementById(g)) return;
5
+ const A = document.createElement("style");
6
+ A.id = g, A.textContent = `
7
+ @keyframes scan-sweep {
8
+ 0%, 100% { top: 0; }
9
+ 50% { top: calc(100% - 2px); }
10
+ }
11
+
12
+ .scan-overlay {
13
+ position: absolute;
14
+ inset: 0;
15
+ pointer-events: none;
16
+ z-index: 10;
17
+ }
18
+
19
+ .scan-overlay__mask {
20
+ position: absolute;
21
+ background: ${y};
22
+ }
23
+
24
+ .scan-overlay__region {
25
+ position: absolute;
26
+ /* Dimensions set via CSS custom properties from JS */
27
+ width: var(--scan-w);
28
+ height: var(--scan-h);
29
+ left: var(--scan-x);
30
+ top: var(--scan-y);
31
+ }
32
+
33
+ .scan-overlay__bracket {
34
+ position: absolute;
35
+ width: 24px;
36
+ height: 24px;
37
+ border-color: ${x};
38
+ border-style: solid;
39
+ border-width: 0;
40
+ }
41
+
42
+ /* Four corners: each gets two border sides to form an L-shape */
43
+ .scan-overlay__bracket--tl {
44
+ top: -1px; left: -1px;
45
+ border-top-width: 3px;
46
+ border-left-width: 3px;
47
+ border-top-left-radius: 4px;
48
+ }
49
+ .scan-overlay__bracket--tr {
50
+ top: -1px; right: -1px;
51
+ border-top-width: 3px;
52
+ border-right-width: 3px;
53
+ border-top-right-radius: 4px;
54
+ }
55
+ .scan-overlay__bracket--bl {
56
+ bottom: -1px; left: -1px;
57
+ border-bottom-width: 3px;
58
+ border-left-width: 3px;
59
+ border-bottom-left-radius: 4px;
60
+ }
61
+ .scan-overlay__bracket--br {
62
+ bottom: -1px; right: -1px;
63
+ border-bottom-width: 3px;
64
+ border-right-width: 3px;
65
+ border-bottom-right-radius: 4px;
66
+ }
67
+
68
+ .scan-overlay__line {
69
+ position: absolute;
70
+ left: 10%;
71
+ width: 80%;
72
+ height: 2px;
73
+ background: ${f};
74
+ box-shadow: 0 0 8px ${f}, 0 0 24px ${f};
75
+ animation: scan-sweep 2.5s ease-in-out infinite;
76
+ border-radius: 1px;
77
+ }
78
+ `, document.head.appendChild(A);
79
+ }
80
+ function I(A, t, e, o, i) {
81
+ const s = [
82
+ // Top bar: full width, from top to scan region top
83
+ { top: "0", left: "0", width: "100%", height: e },
84
+ // Bottom bar: full width, from scan region bottom to container bottom
85
+ { top: `calc(${e} + ${i})`, left: "0", width: "100%", height: `calc(100% - ${e} - ${i})` },
86
+ // Left strip: between top and bottom bars
87
+ { top: e, left: "0", width: t, height: i },
88
+ // Right strip: between top and bottom bars
89
+ { top: e, left: `calc(${t} + ${o})`, width: `calc(100% - ${t} - ${o})`, height: i }
90
+ ];
91
+ for (const h of s) {
92
+ const a = document.createElement("div");
93
+ a.className = "scan-overlay__mask", Object.assign(a.style, h), A.appendChild(a);
94
+ }
95
+ }
96
+ function S(A, t) {
97
+ E();
98
+ const e = document.createElement("div");
99
+ e.className = "scan-overlay";
100
+ const o = `${(t.widthRatio * 100).toFixed(2)}%`, i = `${(t.heightRatio * 100).toFixed(2)}%`, s = `${((1 - t.widthRatio) / 2 * 100).toFixed(2)}%`, h = `${((1 - t.heightRatio) / 2 * 100).toFixed(2)}%`;
101
+ e.style.setProperty("--scan-w", o), e.style.setProperty("--scan-h", i), e.style.setProperty("--scan-x", s), e.style.setProperty("--scan-y", h), I(e, s, h, o, i);
102
+ const a = document.createElement("div");
103
+ a.className = "scan-overlay__region";
104
+ const d = ["tl", "tr", "bl", "br"];
105
+ for (const r of d) {
106
+ const c = document.createElement("div");
107
+ c.className = `scan-overlay__bracket scan-overlay__bracket--${r}`, a.appendChild(c);
108
+ }
109
+ const n = document.createElement("div");
110
+ return n.className = "scan-overlay__line", a.appendChild(n), e.appendChild(a), A.appendChild(e), e;
111
+ }
112
+ function R(A) {
113
+ A.remove();
114
+ }
115
+ const k = "data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=", u = [0, Math.PI / 6, -Math.PI / 6];
116
+ class Q {
117
+ constructor(t) {
118
+ var e, o;
119
+ this._isRunning = !1, this.video = null, this.polyCanvas = null, this.polyCtx = null, this.offscreen = null, this.offCtx = null, this.overlayRoot = null, this.stream = null, this.timerId = null, this.wasmApi = null, this.beepAudio = null, this.previewCanvas = null, this.previewCtx = null, this.cameraWidth = 0, this.cameraHeight = 0, this.barcodeWidth = 0, this.barcodeHeight = 0, this.barcodeOffsetX = 0, this.barcodeOffsetY = 0, this.detectedThisTick = !1, this.container = t.container, this.onDetect = t.onDetect, this.onError = t.onError ?? ((i) => console.error("[BarcodeScanner]", i)), this.scanInterval = t.scanInterval ?? 150, this.beepOnDetect = t.beepOnDetect ?? !0, this.facingMode = t.facingMode ?? "environment", this.regionWidth = ((e = t.scanRegion) == null ? void 0 : e.width) ?? 0.702, this.regionHeight = ((o = t.scanRegion) == null ? void 0 : o.height) ?? 0.242, this.previewCanvas = t.previewCanvas ?? null;
120
+ }
121
+ get isRunning() {
122
+ return this._isRunning;
123
+ }
124
+ // ── Public lifecycle ────────────────────────────────────────────
125
+ async start() {
126
+ if (!this._isRunning)
127
+ try {
128
+ this.setupDOM(), await this.loadWasm(), await this.startCamera(), this.setupResultHandler(), this.startScanLoop(), this._isRunning = !0;
129
+ } catch (t) {
130
+ this.stop();
131
+ const e = t instanceof Error ? t : new Error(String(t));
132
+ this.onError(e);
133
+ }
134
+ }
135
+ stop() {
136
+ this.stopScanLoop(), this.stopCamera(), this.teardownDOM(), this._isRunning = !1;
137
+ }
138
+ /**
139
+ * Toggle the device torch (flashlight). Returns the new torch state.
140
+ * Only works on devices that support the torch constraint (most Android phones).
141
+ */
142
+ async toggleTorch() {
143
+ var s;
144
+ const t = (s = this.stream) == null ? void 0 : s.getVideoTracks()[0];
145
+ if (!t || !t.getCapabilities().torch) return !1;
146
+ const i = !t.getSettings().torch;
147
+ return await t.applyConstraints({
148
+ advanced: [{ torch: i }]
149
+ }), i;
150
+ }
151
+ // ── DOM setup / teardown ────────────────────────────────────────
152
+ setupDOM() {
153
+ getComputedStyle(this.container).position === "static" && (this.container.style.position = "relative"), this.cameraWidth = this.container.clientWidth, this.cameraHeight = this.container.clientHeight, this.barcodeWidth = Math.floor(this.cameraWidth * this.regionWidth), this.barcodeHeight = Math.floor(this.cameraHeight * this.regionHeight), this.barcodeOffsetX = Math.floor((this.cameraWidth - this.barcodeWidth) / 2), this.barcodeOffsetY = Math.floor((this.cameraHeight - this.barcodeHeight) / 2), this.video = document.createElement("video"), Object.assign(this.video.style, {
154
+ width: "100%",
155
+ height: "100%",
156
+ objectFit: "cover"
157
+ }), this.video.setAttribute("autoplay", ""), this.video.setAttribute("muted", ""), this.video.setAttribute("playsinline", ""), this.video.muted = !0, this.container.appendChild(this.video), this.polyCanvas = document.createElement("canvas"), this.polyCanvas.width = this.cameraWidth, this.polyCanvas.height = this.cameraHeight, Object.assign(this.polyCanvas.style, {
158
+ position: "absolute",
159
+ left: "0",
160
+ top: "0",
161
+ pointerEvents: "none"
162
+ }), this.container.appendChild(this.polyCanvas), this.polyCtx = this.polyCanvas.getContext("2d"), this.offscreen = new OffscreenCanvas(this.barcodeWidth * 2, this.barcodeHeight * 2), this.offCtx = this.offscreen.getContext("2d", { willReadFrequently: !0 }), this.overlayRoot = S(this.container, {
163
+ widthRatio: this.regionWidth,
164
+ heightRatio: this.regionHeight
165
+ }), this.beepOnDetect && (this.beepAudio = new Audio(k)), this.previewCanvas && (this.previewCanvas.width = this.barcodeWidth * 2, this.previewCanvas.height = this.barcodeHeight * 2 * u.length, this.previewCtx = this.previewCanvas.getContext("2d"));
166
+ }
167
+ teardownDOM() {
168
+ var t, e;
169
+ this.overlayRoot && (R(this.overlayRoot), this.overlayRoot = null), (t = this.video) == null || t.remove(), this.video = null, (e = this.polyCanvas) == null || e.remove(), this.polyCanvas = null, this.polyCtx = null, this.offscreen = null, this.offCtx = null, this.beepAudio = null, this.previewCtx = null;
170
+ }
171
+ // ── WASM loading ────────────────────────────────────────────────
172
+ /**
173
+ * Wait for the Emscripten Module to finish initializing.
174
+ * The script is loaded via a classic <script> tag in index.html,
175
+ * so by the time start() runs it may already be ready.
176
+ */
177
+ loadWasm() {
178
+ return new Promise((t, e) => {
179
+ const o = setTimeout(() => e(new Error("WASM load timeout")), 15e3), i = () => {
180
+ clearTimeout(o);
181
+ try {
182
+ const s = Module.cwrap;
183
+ if (!s) throw new Error("Module.cwrap not available after runtime init");
184
+ this.wasmApi = {
185
+ scan_image: s("scan_image", "", ["number", "number", "number"]),
186
+ create_buffer: s("create_buffer", "number", ["number", "number"]),
187
+ destroy_buffer: s("destroy_buffer", "", ["number"])
188
+ }, t();
189
+ } catch (s) {
190
+ e(s);
191
+ }
192
+ };
193
+ if (Module.cwrap)
194
+ i();
195
+ else {
196
+ const s = Module.onRuntimeInitialized;
197
+ Module.onRuntimeInitialized = () => {
198
+ s == null || s(), i();
199
+ };
200
+ }
201
+ });
202
+ }
203
+ // ── Camera ──────────────────────────────────────────────────────
204
+ async startCamera() {
205
+ if (!this.video) throw new Error("DOM not set up");
206
+ const t = this.cameraWidth * 2, e = {
207
+ video: {
208
+ width: t,
209
+ height: t,
210
+ facingMode: this.facingMode,
211
+ resizeMode: "crop-and-scale",
212
+ aspectRatio: { exact: 1 }
213
+ }
214
+ };
215
+ this.stream = await navigator.mediaDevices.getUserMedia(e), this.video.srcObject = this.stream, await this.video.play();
216
+ }
217
+ stopCamera() {
218
+ if (this.stream) {
219
+ for (const t of this.stream.getTracks())
220
+ t.stop();
221
+ this.stream = null;
222
+ }
223
+ this.video && (this.video.srcObject = null);
224
+ }
225
+ // ── Result handler ──────────────────────────────────────────────
226
+ /**
227
+ * Wire up Module.processResult so the WASM-side library.js callback
228
+ * routes detected barcodes back into our class.
229
+ */
230
+ setupResultHandler() {
231
+ const t = (e, o, i) => {
232
+ if (!o) return;
233
+ this.detectedThisTick = !0;
234
+ const s = [];
235
+ for (let a = 0; a < i.length; a += 2)
236
+ s.push(i[a] / 2 + this.barcodeOffsetX), s.push(i[a + 1] / 2 + this.barcodeOffsetY);
237
+ this.drawPoly(s), this.beepOnDetect && this.beepAudio && this.beepAudio.play().catch(() => {
238
+ });
239
+ const h = { symbol: e, data: o, polygon: s };
240
+ this.onDetect(h);
241
+ };
242
+ Module.processResult = t;
243
+ }
244
+ // ── Scan loop ───────────────────────────────────────────────────
245
+ startScanLoop() {
246
+ this.timerId = setInterval(() => this.scanTick(), this.scanInterval);
247
+ }
248
+ stopScanLoop() {
249
+ this.timerId !== null && (clearInterval(this.timerId), this.timerId = null);
250
+ }
251
+ /**
252
+ * One scan tick: capture the barcode region, convert to grayscale,
253
+ * and run through WASM. Uses rotation-based skew correction —
254
+ * tries 0° first, then ±30° only if no barcode was found.
255
+ */
256
+ scanTick() {
257
+ if (!this.video || !this.offCtx || !this.offscreen || !this.wasmApi || !this.polyCtx) return;
258
+ const t = this.video.videoWidth, e = this.video.videoHeight;
259
+ if (t === 0 || e === 0) return;
260
+ const o = t / this.cameraWidth, i = e / this.cameraHeight, s = this.barcodeOffsetX * o, h = this.barcodeOffsetY * i, a = this.barcodeWidth * o, d = this.barcodeHeight * i;
261
+ this.polyCtx.clearRect(0, 0, this.cameraWidth, this.cameraHeight);
262
+ const n = this.offscreen.width, r = this.offscreen.height;
263
+ this.previewCtx && this.previewCtx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);
264
+ for (let c = 0; c < u.length; c++) {
265
+ const p = u[c];
266
+ if (this.detectedThisTick = !1, this.offCtx.save(), p !== 0 && (this.offCtx.translate(n / 2, r / 2), this.offCtx.rotate(p), this.offCtx.translate(-n / 2, -r / 2)), this.offCtx.drawImage(
267
+ this.video,
268
+ s,
269
+ h,
270
+ a,
271
+ d,
272
+ 0,
273
+ 0,
274
+ n,
275
+ r
276
+ ), this.offCtx.restore(), this.previewCtx) {
277
+ const l = c * r;
278
+ this.previewCtx.drawImage(this.offscreen, 0, 0, n, r, 0, l, n, r);
279
+ }
280
+ const C = this.offCtx.getImageData(0, 0, n, r), w = this.toGrayscale(C.data), m = this.wasmApi.create_buffer(n, r);
281
+ if (Module.HEAP8.set(w, m), this.wasmApi.scan_image(m, n, r), this.previewCtx) {
282
+ const l = c * r, v = Math.round(p * 180 / Math.PI), b = `${v >= 0 ? "+" : ""}${v}°`;
283
+ this.detectedThisTick && (this.previewCtx.strokeStyle = "#00ff88", this.previewCtx.lineWidth = 3, this.previewCtx.strokeRect(1, l + 1, n - 2, r - 2)), this.previewCtx.font = "bold 16px monospace", this.previewCtx.fillStyle = this.detectedThisTick ? "#00ff88" : "rgba(255,255,255,0.7)", this.previewCtx.fillText(b, 8, l + 22);
284
+ }
285
+ if (this.detectedThisTick) break;
286
+ }
287
+ }
288
+ /**
289
+ * Convert RGBA pixel data to grayscale using the BT.601 luma formula.
290
+ * Uses integer arithmetic for speed (same formula as the original index.js).
291
+ */
292
+ toGrayscale(t) {
293
+ const e = new Uint8Array(t.length / 4);
294
+ for (let o = 0, i = 0; o < t.length; o += 4, i++)
295
+ e[i] = t[o] * 66 + t[o + 1] * 129 + t[o + 2] * 25 + 4096 >> 8;
296
+ return e;
297
+ }
298
+ // ── Drawing ─────────────────────────────────────────────────────
299
+ /** Draw a polygon outline on the overlay canvas. */
300
+ drawPoly(t) {
301
+ if (!(!this.polyCtx || t.length < 4)) {
302
+ this.polyCtx.beginPath(), this.polyCtx.moveTo(t[0], t[1]);
303
+ for (let e = 2; e < t.length; e += 2)
304
+ this.polyCtx.lineTo(t[e], t[e + 1]);
305
+ this.polyCtx.closePath(), this.polyCtx.lineWidth = 2, this.polyCtx.strokeStyle = "#FF0000", this.polyCtx.stroke();
306
+ }
307
+ }
308
+ }
309
+ export {
310
+ Q as BarcodeScanner,
311
+ S as createOverlay,
312
+ R as removeOverlay
313
+ };
@@ -0,0 +1,73 @@
1
+ (function(h,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(h=typeof globalThis<"u"?globalThis:h||self,d(h.WebWasmBarcodeReader={}))})(this,(function(h){"use strict";const d="barcode-scanner-overlay-styles",y="rgba(0, 0, 0, 0.55)",x="#00ff88",f="#00ff88";function E(){if(document.getElementById(d))return;const A=document.createElement("style");A.id=d,A.textContent=`
2
+ @keyframes scan-sweep {
3
+ 0%, 100% { top: 0; }
4
+ 50% { top: calc(100% - 2px); }
5
+ }
6
+
7
+ .scan-overlay {
8
+ position: absolute;
9
+ inset: 0;
10
+ pointer-events: none;
11
+ z-index: 10;
12
+ }
13
+
14
+ .scan-overlay__mask {
15
+ position: absolute;
16
+ background: ${y};
17
+ }
18
+
19
+ .scan-overlay__region {
20
+ position: absolute;
21
+ /* Dimensions set via CSS custom properties from JS */
22
+ width: var(--scan-w);
23
+ height: var(--scan-h);
24
+ left: var(--scan-x);
25
+ top: var(--scan-y);
26
+ }
27
+
28
+ .scan-overlay__bracket {
29
+ position: absolute;
30
+ width: 24px;
31
+ height: 24px;
32
+ border-color: ${x};
33
+ border-style: solid;
34
+ border-width: 0;
35
+ }
36
+
37
+ /* Four corners: each gets two border sides to form an L-shape */
38
+ .scan-overlay__bracket--tl {
39
+ top: -1px; left: -1px;
40
+ border-top-width: 3px;
41
+ border-left-width: 3px;
42
+ border-top-left-radius: 4px;
43
+ }
44
+ .scan-overlay__bracket--tr {
45
+ top: -1px; right: -1px;
46
+ border-top-width: 3px;
47
+ border-right-width: 3px;
48
+ border-top-right-radius: 4px;
49
+ }
50
+ .scan-overlay__bracket--bl {
51
+ bottom: -1px; left: -1px;
52
+ border-bottom-width: 3px;
53
+ border-left-width: 3px;
54
+ border-bottom-left-radius: 4px;
55
+ }
56
+ .scan-overlay__bracket--br {
57
+ bottom: -1px; right: -1px;
58
+ border-bottom-width: 3px;
59
+ border-right-width: 3px;
60
+ border-bottom-right-radius: 4px;
61
+ }
62
+
63
+ .scan-overlay__line {
64
+ position: absolute;
65
+ left: 10%;
66
+ width: 80%;
67
+ height: 2px;
68
+ background: ${f};
69
+ box-shadow: 0 0 8px ${f}, 0 0 24px ${f};
70
+ animation: scan-sweep 2.5s ease-in-out infinite;
71
+ border-radius: 1px;
72
+ }
73
+ `,document.head.appendChild(A)}function I(A,t,e,o,i){const s=[{top:"0",left:"0",width:"100%",height:e},{top:`calc(${e} + ${i})`,left:"0",width:"100%",height:`calc(100% - ${e} - ${i})`},{top:e,left:"0",width:t,height:i},{top:e,left:`calc(${t} + ${o})`,width:`calc(100% - ${t} - ${o})`,height:i}];for(const c of s){const a=document.createElement("div");a.className="scan-overlay__mask",Object.assign(a.style,c),A.appendChild(a)}}function g(A,t){E();const e=document.createElement("div");e.className="scan-overlay";const o=`${(t.widthRatio*100).toFixed(2)}%`,i=`${(t.heightRatio*100).toFixed(2)}%`,s=`${((1-t.widthRatio)/2*100).toFixed(2)}%`,c=`${((1-t.heightRatio)/2*100).toFixed(2)}%`;e.style.setProperty("--scan-w",o),e.style.setProperty("--scan-h",i),e.style.setProperty("--scan-x",s),e.style.setProperty("--scan-y",c),I(e,s,c,o,i);const a=document.createElement("div");a.className="scan-overlay__region";const m=["tl","tr","bl","br"];for(const r of m){const l=document.createElement("div");l.className=`scan-overlay__bracket scan-overlay__bracket--${r}`,a.appendChild(l)}const n=document.createElement("div");return n.className="scan-overlay__line",a.appendChild(n),e.appendChild(a),A.appendChild(e),e}function C(A){A.remove()}const S="data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=",u=[0,Math.PI/6,-Math.PI/6];class R{constructor(t){var e,o;this._isRunning=!1,this.video=null,this.polyCanvas=null,this.polyCtx=null,this.offscreen=null,this.offCtx=null,this.overlayRoot=null,this.stream=null,this.timerId=null,this.wasmApi=null,this.beepAudio=null,this.previewCanvas=null,this.previewCtx=null,this.cameraWidth=0,this.cameraHeight=0,this.barcodeWidth=0,this.barcodeHeight=0,this.barcodeOffsetX=0,this.barcodeOffsetY=0,this.detectedThisTick=!1,this.container=t.container,this.onDetect=t.onDetect,this.onError=t.onError??(i=>console.error("[BarcodeScanner]",i)),this.scanInterval=t.scanInterval??150,this.beepOnDetect=t.beepOnDetect??!0,this.facingMode=t.facingMode??"environment",this.regionWidth=((e=t.scanRegion)==null?void 0:e.width)??.702,this.regionHeight=((o=t.scanRegion)==null?void 0:o.height)??.242,this.previewCanvas=t.previewCanvas??null}get isRunning(){return this._isRunning}async start(){if(!this._isRunning)try{this.setupDOM(),await this.loadWasm(),await this.startCamera(),this.setupResultHandler(),this.startScanLoop(),this._isRunning=!0}catch(t){this.stop();const e=t instanceof Error?t:new Error(String(t));this.onError(e)}}stop(){this.stopScanLoop(),this.stopCamera(),this.teardownDOM(),this._isRunning=!1}async toggleTorch(){var s;const t=(s=this.stream)==null?void 0:s.getVideoTracks()[0];if(!t||!t.getCapabilities().torch)return!1;const i=!t.getSettings().torch;return await t.applyConstraints({advanced:[{torch:i}]}),i}setupDOM(){getComputedStyle(this.container).position==="static"&&(this.container.style.position="relative"),this.cameraWidth=this.container.clientWidth,this.cameraHeight=this.container.clientHeight,this.barcodeWidth=Math.floor(this.cameraWidth*this.regionWidth),this.barcodeHeight=Math.floor(this.cameraHeight*this.regionHeight),this.barcodeOffsetX=Math.floor((this.cameraWidth-this.barcodeWidth)/2),this.barcodeOffsetY=Math.floor((this.cameraHeight-this.barcodeHeight)/2),this.video=document.createElement("video"),Object.assign(this.video.style,{width:"100%",height:"100%",objectFit:"cover"}),this.video.setAttribute("autoplay",""),this.video.setAttribute("muted",""),this.video.setAttribute("playsinline",""),this.video.muted=!0,this.container.appendChild(this.video),this.polyCanvas=document.createElement("canvas"),this.polyCanvas.width=this.cameraWidth,this.polyCanvas.height=this.cameraHeight,Object.assign(this.polyCanvas.style,{position:"absolute",left:"0",top:"0",pointerEvents:"none"}),this.container.appendChild(this.polyCanvas),this.polyCtx=this.polyCanvas.getContext("2d"),this.offscreen=new OffscreenCanvas(this.barcodeWidth*2,this.barcodeHeight*2),this.offCtx=this.offscreen.getContext("2d",{willReadFrequently:!0}),this.overlayRoot=g(this.container,{widthRatio:this.regionWidth,heightRatio:this.regionHeight}),this.beepOnDetect&&(this.beepAudio=new Audio(S)),this.previewCanvas&&(this.previewCanvas.width=this.barcodeWidth*2,this.previewCanvas.height=this.barcodeHeight*2*u.length,this.previewCtx=this.previewCanvas.getContext("2d"))}teardownDOM(){var t,e;this.overlayRoot&&(C(this.overlayRoot),this.overlayRoot=null),(t=this.video)==null||t.remove(),this.video=null,(e=this.polyCanvas)==null||e.remove(),this.polyCanvas=null,this.polyCtx=null,this.offscreen=null,this.offCtx=null,this.beepAudio=null,this.previewCtx=null}loadWasm(){return new Promise((t,e)=>{const o=setTimeout(()=>e(new Error("WASM load timeout")),15e3),i=()=>{clearTimeout(o);try{const s=Module.cwrap;if(!s)throw new Error("Module.cwrap not available after runtime init");this.wasmApi={scan_image:s("scan_image","",["number","number","number"]),create_buffer:s("create_buffer","number",["number","number"]),destroy_buffer:s("destroy_buffer","",["number"])},t()}catch(s){e(s)}};if(Module.cwrap)i();else{const s=Module.onRuntimeInitialized;Module.onRuntimeInitialized=()=>{s==null||s(),i()}}})}async startCamera(){if(!this.video)throw new Error("DOM not set up");const t=this.cameraWidth*2,e={video:{width:t,height:t,facingMode:this.facingMode,resizeMode:"crop-and-scale",aspectRatio:{exact:1}}};this.stream=await navigator.mediaDevices.getUserMedia(e),this.video.srcObject=this.stream,await this.video.play()}stopCamera(){if(this.stream){for(const t of this.stream.getTracks())t.stop();this.stream=null}this.video&&(this.video.srcObject=null)}setupResultHandler(){const t=(e,o,i)=>{if(!o)return;this.detectedThisTick=!0;const s=[];for(let a=0;a<i.length;a+=2)s.push(i[a]/2+this.barcodeOffsetX),s.push(i[a+1]/2+this.barcodeOffsetY);this.drawPoly(s),this.beepOnDetect&&this.beepAudio&&this.beepAudio.play().catch(()=>{});const c={symbol:e,data:o,polygon:s};this.onDetect(c)};Module.processResult=t}startScanLoop(){this.timerId=setInterval(()=>this.scanTick(),this.scanInterval)}stopScanLoop(){this.timerId!==null&&(clearInterval(this.timerId),this.timerId=null)}scanTick(){if(!this.video||!this.offCtx||!this.offscreen||!this.wasmApi||!this.polyCtx)return;const t=this.video.videoWidth,e=this.video.videoHeight;if(t===0||e===0)return;const o=t/this.cameraWidth,i=e/this.cameraHeight,s=this.barcodeOffsetX*o,c=this.barcodeOffsetY*i,a=this.barcodeWidth*o,m=this.barcodeHeight*i;this.polyCtx.clearRect(0,0,this.cameraWidth,this.cameraHeight);const n=this.offscreen.width,r=this.offscreen.height;this.previewCtx&&this.previewCtx.clearRect(0,0,this.previewCanvas.width,this.previewCanvas.height);for(let l=0;l<u.length;l++){const v=u[l];if(this.detectedThisTick=!1,this.offCtx.save(),v!==0&&(this.offCtx.translate(n/2,r/2),this.offCtx.rotate(v),this.offCtx.translate(-n/2,-r/2)),this.offCtx.drawImage(this.video,s,c,a,m,0,0,n,r),this.offCtx.restore(),this.previewCtx){const p=l*r;this.previewCtx.drawImage(this.offscreen,0,0,n,r,0,p,n,r)}const k=this.offCtx.getImageData(0,0,n,r),Q=this.toGrayscale(k.data),b=this.wasmApi.create_buffer(n,r);if(Module.HEAP8.set(Q,b),this.wasmApi.scan_image(b,n,r),this.previewCtx){const p=l*r,w=Math.round(v*180/Math.PI),W=`${w>=0?"+":""}${w}°`;this.detectedThisTick&&(this.previewCtx.strokeStyle="#00ff88",this.previewCtx.lineWidth=3,this.previewCtx.strokeRect(1,p+1,n-2,r-2)),this.previewCtx.font="bold 16px monospace",this.previewCtx.fillStyle=this.detectedThisTick?"#00ff88":"rgba(255,255,255,0.7)",this.previewCtx.fillText(W,8,p+22)}if(this.detectedThisTick)break}}toGrayscale(t){const e=new Uint8Array(t.length/4);for(let o=0,i=0;o<t.length;o+=4,i++)e[i]=t[o]*66+t[o+1]*129+t[o+2]*25+4096>>8;return e}drawPoly(t){if(!(!this.polyCtx||t.length<4)){this.polyCtx.beginPath(),this.polyCtx.moveTo(t[0],t[1]);for(let e=2;e<t.length;e+=2)this.polyCtx.lineTo(t[e],t[e+1]);this.polyCtx.closePath(),this.polyCtx.lineWidth=2,this.polyCtx.strokeStyle="#FF0000",this.polyCtx.stroke()}}}h.BarcodeScanner=R,h.createOverlay=g,h.removeOverlay=C,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "web-wasm-barcode-reader",
3
+ "version": "1.0.0",
4
+ "description": "Barcode scanner powered by ZBar compiled to WebAssembly",
5
+ "keywords": ["barcode", "scanner", "wasm", "webassembly", "zbar", "qr-code"],
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/niclasku/web-wasm-barcode-reader.git"
10
+ },
11
+ "type": "module",
12
+ "main": "dist/web-wasm-barcode-reader.umd.cjs",
13
+ "module": "dist/web-wasm-barcode-reader.js",
14
+ "types": "dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/web-wasm-barcode-reader.js",
19
+ "require": "./dist/web-wasm-barcode-reader.umd.cjs"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "public/a.out.js",
25
+ "public/a.out.wasm"
26
+ ],
27
+ "sideEffects": false,
28
+ "scripts": {
29
+ "dev": "vite",
30
+ "build": "tsc --noEmit && vite build",
31
+ "build:lib": "vite build --config vite.lib.config.ts && tsc --project tsconfig.lib.json",
32
+ "preview": "vite preview",
33
+ "prepublishOnly": "npm run build:lib"
34
+ },
35
+ "devDependencies": {
36
+ "typescript": "^5.7.0",
37
+ "vite": "^6.0.0"
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=(function(status,toThrow){throw toThrow});Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;if(Module["ENVIRONMENT"]){if(Module["ENVIRONMENT"]==="WEB"){ENVIRONMENT_IS_WEB=true}else if(Module["ENVIRONMENT"]==="WORKER"){ENVIRONMENT_IS_WORKER=true}else if(Module["ENVIRONMENT"]==="NODE"){ENVIRONMENT_IS_NODE=true}else if(Module["ENVIRONMENT"]==="SHELL"){ENVIRONMENT_IS_SHELL=true}else{throw new Error("Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.")}}else{ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER}if(ENVIRONMENT_IS_NODE){var nodeFS;var nodePath;Module["read"]=function shell_read(filename,binary){var ret;if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);ret=nodeFS["readFileSync"](filename);return binary?ret:ret.toString()};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));process["on"]("unhandledRejection",(function(reason,p){process["exit"](1)}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){Module["read"]=function shell_read(f){return read(f)}}Module["readBinary"]=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof quit==="function"){Module["quit"]=(function(status,toThrow){quit(status)})}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=(function(title){document.title=title})}Module["print"]=typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null;Module["printErr"]=typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||Module["print"];Module.print=Module["print"];Module.printErr=Module["printErr"];for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var STACK_ALIGN=16;function staticAlloc(size){assert(!staticSealed);var ret=STATICTOP;STATICTOP=STATICTOP+size+15&-16;return ret}function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;var ret=size=Math.ceil(size/factor)*factor;return ret}var functionPointers=new Array(0);var GLOBAL_BASE=1024;var ABORT=0;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}function getCFunc(ident){var func=Module["_"+ident];assert(func,"Cannot call unknown function "+ident+", make sure it is exported");return func}var JSfuncs={"stackSave":(function(){stackSave()}),"stackRestore":(function(){stackRestore()}),"arrayToC":(function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};function ccall(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);else if(returnType==="boolean")ret=Boolean(ret);if(stack!==0){stackRestore(stack)}return ret}function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}return(function(){return ccall(ident,returnType,argTypes,arguments)})}function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return UTF8ToString(ptr)}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx){var endPtr=idx;while(u8Array[endPtr])++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}}function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;var ASMJS_PAGE_SIZE=16777216;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed;var STACK_BASE,STACKTOP,STACK_MAX;var DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0;staticSealed=false;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY<TOTAL_STACK)Module.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){Module["wasmMemory"]=new WebAssembly.Memory({"initial":TOTAL_MEMORY/WASM_PAGE_SIZE,"maximum":TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=Module["wasmMemory"].buffer}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();function getTotalMemory(){return TOTAL_MEMORY}HEAP32[0]=1668509029;HEAP16[1]=25459;if(HEAPU8[2]!==115||HEAPU8[3]!==99)throw"Runtime error: expected the system to be little-endian!";function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_round=Math.round;var Math_min=Math.min;var Math_max=Math.max;var Math_clz32=Math.clz32;var Math_trunc=Math.trunc;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}function integrateWasmJS(){var wasmTextFile="a.out.wast";var wasmBinaryFile="a.out.wasm";var asmjsCodeFile="a.out.temp.asm.js";if(typeof Module["locateFile"]==="function"){if(!isDataURI(wasmTextFile)){wasmTextFile=Module["locateFile"](wasmTextFile)}if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=Module["locateFile"](wasmBinaryFile)}if(!isDataURI(asmjsCodeFile)){asmjsCodeFile=Module["locateFile"](asmjsCodeFile)}}var wasmPageSize=64*1024;var info={"global":null,"env":null,"asm2wasm":{"f64-rem":(function(x,y){return x%y}),"debugger":(function(){debugger})},"parent":Module};var exports=null;function mergeMemory(newBuffer){var oldBuffer=Module["buffer"];if(newBuffer.byteLength<oldBuffer.byteLength){Module["printErr"]("the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here")}var oldView=new Int8Array(oldBuffer);var newView=new Int8Array(newBuffer);newView.set(oldView);updateGlobalBuffer(newBuffer);updateGlobalBufferViews()}function fixImports(imports){return imports}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then((function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()})).catch((function(){return getBinary()}))}return new Promise((function(resolve,reject){resolve(getBinary())}))}function doNativeWasm(global,env,providedBuffer){if(typeof WebAssembly!=="object"){Module["printErr"]("no native wasm support detected");return false}if(!(Module["wasmMemory"]instanceof WebAssembly.Memory)){Module["printErr"]("no native wasm Memory in use");return false}env["memory"]=Module["wasmMemory"];info["global"]={"NaN":NaN,"Infinity":Infinity};info["global.Math"]=Math;info["env"]=env;function receiveInstance(instance,module){exports=instance.exports;if(exports.memory)mergeMemory(exports.memory);Module["asm"]=exports;Module["usingWasm"]=true;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){Module["printErr"]("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"],output["module"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then((function(binary){return WebAssembly.instantiate(binary,info)})).then(receiver).catch((function(reason){Module["printErr"]("failed to asynchronously prepare wasm: "+reason);abort(reason)}))}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource).catch((function(reason){Module["printErr"]("wasm streaming compile failed: "+reason);Module["printErr"]("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)}))}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asmPreload"]=Module["asm"];var asmjsReallocBuffer=Module["reallocBuffer"];var wasmReallocBuffer=(function(size){var PAGE_MULTIPLE=Module["usingWasm"]?WASM_PAGE_SIZE:ASMJS_PAGE_SIZE;size=alignUp(size,PAGE_MULTIPLE);var old=Module["buffer"];var oldSize=old.byteLength;if(Module["usingWasm"]){try{var result=Module["wasmMemory"].grow((size-oldSize)/wasmPageSize);if(result!==(-1|0)){return Module["buffer"]=Module["wasmMemory"].buffer}else{return null}}catch(e){return null}}});Module["reallocBuffer"]=(function(size){if(finalMethod==="asmjs"){return asmjsReallocBuffer(size)}else{return wasmReallocBuffer(size)}});var finalMethod="";Module["asm"]=(function(global,env,providedBuffer){env=fixImports(env);if(!env["table"]){var TABLE_SIZE=Module["wasmTableSize"];if(TABLE_SIZE===undefined)TABLE_SIZE=1024;var MAX_TABLE_SIZE=Module["wasmMaxTableSize"];if(typeof WebAssembly==="object"&&typeof WebAssembly.Table==="function"){if(MAX_TABLE_SIZE!==undefined){env["table"]=new WebAssembly.Table({"initial":TABLE_SIZE,"maximum":MAX_TABLE_SIZE,"element":"anyfunc"})}else{env["table"]=new WebAssembly.Table({"initial":TABLE_SIZE,element:"anyfunc"})}}else{env["table"]=new Array(TABLE_SIZE)}Module["wasmTable"]=env["table"]}if(!env["memoryBase"]){env["memoryBase"]=Module["STATIC_BASE"]}if(!env["tableBase"]){env["tableBase"]=0}var exports;exports=doNativeWasm(global,env,providedBuffer);if(!exports)abort("no binaryen method succeeded. consider enabling more options, like interpreting, if you want that: https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods");return exports})}integrateWasmJS();STATIC_BASE=GLOBAL_BASE;STATICTOP=STATIC_BASE+136976;__ATINIT__.push();var STATIC_BUMP=136976;Module["STATIC_BASE"]=STATIC_BASE;Module["STATIC_BUMP"]=STATIC_BUMP;STATICTOP+=16;function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+Pointer_stringify(condition)+", at: "+[filename?Pointer_stringify(filename):"unknown filename",line,func?Pointer_stringify(func):"unknown function"])}var SYSCALLS={varargs:0,get:(function(varargs){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffers){___syscall146.buffers=[null,[],[]];___syscall146.printChar=(function(stream,curr){var buffer=___syscall146.buffers[stream];assert(buffer);if(curr===0||curr===10){(stream===1?Module["print"]:Module["printErr"])(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}})}for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){___syscall146.printChar(stream,HEAPU8[ptr+j])}ret+=len}return ret}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall54(which,varargs){SYSCALLS.varargs=varargs;try{return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall6(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD();FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function _emscripten_get_now(){abort()}function _emscripten_get_now_is_monotonic(){return ENVIRONMENT_IS_NODE||typeof dateNow!=="undefined"||(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&self["performance"]&&self["performance"]["now"]}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if(clk_id===1&&_emscripten_get_now_is_monotonic()){now=_emscripten_get_now()}else{___setErrNo(ERRNO_CODES.EINVAL);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _js_output_result(symbol,data,polygon,polygon_size){const Pointer_stringify=Module["Pointer_stringify"];const resultView=new Int32Array(Module.HEAP32.buffer,polygon,polygon_size*2);const flatCoords=new Int32Array(resultView);const coordinates=flatCoords;const downstreamProcessor=Module["processResult"];if(downstreamProcessor==null){throw new Error("No downstream processing function set")}downstreamProcessor(Pointer_stringify(symbol),Pointer_stringify(data),coordinates)}function _llvm_stackrestore(p){var self=_llvm_stacksave;var ret=self.LLVM_SAVEDSTACKS[p];self.LLVM_SAVEDSTACKS.splice(p,1);stackRestore(ret)}function _llvm_stacksave(){var self=_llvm_stacksave;if(!self.LLVM_SAVEDSTACKS){self.LLVM_SAVEDSTACKS=[]}self.LLVM_SAVEDSTACKS.push(stackSave());return self.LLVM_SAVEDSTACKS.length-1}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function _emscripten_get_now_actual(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else if(typeof self==="object"&&self["performance"]&&typeof self["performance"]["now"]==="function"){_emscripten_get_now=(function(){return self["performance"]["now"]()})}else if(typeof performance==="object"&&typeof performance["now"]==="function"){_emscripten_get_now=(function(){return performance["now"]()})}else{_emscripten_get_now=Date.now}DYNAMICTOP_PTR=staticAlloc(4);STACK_BASE=STACKTOP=alignMemory(STATICTOP);STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=alignMemory(STACK_MAX);HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;staticSealed=true;Module["wasmTableSize"]=19;Module["wasmMaxTableSize"]=19;Module.asmGlobalArg={};Module.asmLibraryArg={"abort":abort,"enlargeMemory":enlargeMemory,"getTotalMemory":getTotalMemory,"abortOnCannotGrowMemory":abortOnCannotGrowMemory,"___assert_fail":___assert_fail,"___setErrNo":___setErrNo,"___syscall140":___syscall140,"___syscall146":___syscall146,"___syscall54":___syscall54,"___syscall6":___syscall6,"_clock_gettime":_clock_gettime,"_emscripten_memcpy_big":_emscripten_memcpy_big,"_js_output_result":_js_output_result,"_llvm_stackrestore":_llvm_stackrestore,"_llvm_stacksave":_llvm_stacksave,"DYNAMICTOP_PTR":DYNAMICTOP_PTR,"STACKTOP":STACKTOP};var asm=Module["asm"](Module.asmGlobalArg,Module.asmLibraryArg,buffer);Module["asm"]=asm;var ___errno_location=Module["___errno_location"]=(function(){return Module["asm"]["___errno_location"].apply(null,arguments)});var _create_buffer=Module["_create_buffer"]=(function(){return Module["asm"]["_create_buffer"].apply(null,arguments)});var _destroy_buffer=Module["_destroy_buffer"]=(function(){return Module["asm"]["_destroy_buffer"].apply(null,arguments)});var _scan_image=Module["_scan_image"]=(function(){return Module["asm"]["_scan_image"].apply(null,arguments)});var stackAlloc=Module["stackAlloc"]=(function(){return Module["asm"]["stackAlloc"].apply(null,arguments)});var stackRestore=Module["stackRestore"]=(function(){return Module["asm"]["stackRestore"].apply(null,arguments)});var stackSave=Module["stackSave"]=(function(){return Module["asm"]["stackSave"].apply(null,arguments)});var dynCall_vi=Module["dynCall_vi"]=(function(){return Module["asm"]["dynCall_vi"].apply(null,arguments)});Module["asm"]=asm;Module["cwrap"]=cwrap;Module["Pointer_stringify"]=Pointer_stringify;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]&&status===0){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=exit;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run()
Binary file