quadqr-js 1.3.0 → 1.5.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/FORMAT.md +13 -11
- package/README.md +84 -48
- package/SPECIFICATION.md +34 -19
- package/bin/quadqr.js +4 -2
- package/dist/camera-scanner-worker.js +793 -0
- package/dist/esm/benchmark.js +37 -6
- package/dist/esm/brotli.js +2210 -0
- package/dist/esm/camera-scanner-worker.js +793 -0
- package/dist/esm/deflate.js +381 -0
- package/dist/esm/geometry.js +25 -10
- package/dist/esm/quadqr.js +1904 -191
- package/dist/esm/security.js +19 -0
- package/dist/esm/vision.js +1118 -175
- package/dist/esm/wasm.js +85 -12
- package/dist/quadqr.js +5728 -408
- package/dist/quadqr.min.js +5621 -430
- package/dist/wasm/quadqr-core.wasm +0 -0
- package/docs/API.md +54 -15
- package/docs/BROWSER_CDN.md +10 -6
- package/docs/CLI.md +167 -156
- package/docs/COMPRESSION.md +170 -0
- package/docs/GETTING_STARTED.md +12 -2
- package/docs/HIGH_DENSITY_MODE.md +100 -100
- package/docs/NODE.md +159 -137
- package/docs/README.md +62 -60
- package/docs/RELIABILITY_LAB.md +64 -0
- package/docs/TRIANGLE16.md +98 -108
- package/docs/WASM.md +30 -4
- package/package.json +12 -11
- package/types/benchmark.d.ts +0 -8
- package/types/index.d.ts +0 -403
- package/types/node.d.ts +0 -11
package/FORMAT.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# QuadQR Format
|
|
1
|
+
# QuadQR Format v6
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
@@ -50,7 +50,7 @@ Three 7×7 black/white finder structures are placed at top-left, top-right, and
|
|
|
50
50
|
|
|
51
51
|
## Alignment patterns
|
|
52
52
|
|
|
53
|
-
QuadQR always keeps exactly three primary 7×7 finder patterns. Larger versions do **not** add more primary finders. Instead, versions 2 through 40 use distributed black/white alignment markers following the same center-position schedule used by standard QR Code versions.
|
|
53
|
+
QuadQR always keeps exactly three primary 7×7 finder patterns. Larger versions do **not** add more primary finders. Instead, versions 2 through 40 use distributed black/white alignment markers following the same center-position schedule used by standard QR Code versions. **Format v6 uses a full 5×5 nested black/white/black alignment eye at every scheduled alignment position**, matching the strong structural signature used by proven QR detectors.
|
|
54
54
|
|
|
55
55
|
The three alignment positions that would overlap the primary finder corners are omitted. This produces progressively more alignment references as the matrix grows. Examples:
|
|
56
56
|
|
|
@@ -64,7 +64,7 @@ v40 -> 46 alignment patterns
|
|
|
64
64
|
|
|
65
65
|
Version 1 is a QuadQR-specific exception. Standard QR v1 has no alignment pattern, but QuadQR keeps one legacy 5×5 bottom-right bootstrap alignment marker with a one-cell white separator so the camera scanner still has a fourth projective reference point.
|
|
66
66
|
|
|
67
|
-
For versions 2 through 40, the scanner uses the 5×5
|
|
67
|
+
For versions 2 through 40, the scanner uses the bottom-right 5×5 member of the distributed alignment grid as the primary fourth homography reference, then scores and refines against the full 5×5 alignment grid. A fast nested-eye locator is attempted before broader template recovery so perspective geometry can be established as soon as the three primary finder eyes are visible.
|
|
68
68
|
|
|
69
69
|
## Timing structures
|
|
70
70
|
|
|
@@ -361,13 +361,13 @@ The decoder also tries legacy physical order as a fallback for older QuadQR matr
|
|
|
361
361
|
|
|
362
362
|
Unused data positions are filled with deterministic pseudo-random values in the range 0..3. Padding is not semantically decoded.
|
|
363
363
|
|
|
364
|
-
##
|
|
364
|
+
## Spectrum ECC 2.0 confidence-aware and soft decoding
|
|
365
365
|
|
|
366
|
-
For image/camera scans, classification retains more than the winning RGBW state. Each sampled
|
|
366
|
+
For image/camera scans, classification retains more than the winning RGBW/Triangle16 state. Each sampled data cell receives a confidence score and a bounded second hypothesis derived from the calibrated palette classification. Triangle16 also incorporates the measured disagreement between its three interior samples per triangle so edge bleed or sub-module geometry instability lowers confidence instead of becoming false certainty.
|
|
367
367
|
|
|
368
|
-
|
|
368
|
+
In normal RGBW mode, one GF(256) byte symbol corresponds to four 2-bit cells. In High Density Mode, a body byte corresponds to two 4-bit Triangle16 cells, while the protected header remains four solid RGBW-equivalent cells per byte. Symbol confidence is the minimum confidence of its constituent cells because an error in any constituent cell changes the reconstructed byte.
|
|
369
369
|
|
|
370
|
-
Decoding first attempts normal hard-decision Reed-Solomon correction. If that fails, low-confidence byte positions are progressively promoted to known erasures and the decoder retries error/erasure RS correction.
|
|
370
|
+
Decoding first attempts normal hard-decision Reed-Solomon correction. If that fails, low-confidence byte positions are progressively promoted to known erasures and the decoder retries error/erasure RS correction. If that still fails and scanner second hypotheses are available, Spectrum ECC 2.0 performs a bounded Chase-style search over the least-confident cells, trying single alternate substitutions and then a small pair set. Every candidate is passed through the unchanged RS and CRC validation path.
|
|
371
371
|
|
|
372
372
|
The RS budget follows the usual error/erasure relationship:
|
|
373
373
|
|
|
@@ -391,11 +391,13 @@ RGB frame
|
|
|
391
391
|
-> distributed alignment-grid validation
|
|
392
392
|
-> projective module sampling
|
|
393
393
|
-> observed black/white/R/G/B calibration
|
|
394
|
-
->
|
|
394
|
+
-> observed-palette / white-balance / affine calibration attempts
|
|
395
|
+
-> RGBW or Triangle16 classification + confidence + second hypothesis
|
|
395
396
|
-> four-state XOR unmasking
|
|
396
397
|
-> reverse spectral-spatial permutation
|
|
397
398
|
-> protected header GF(256) hard RS decode
|
|
398
399
|
-> confidence-guided error/erasure retry when needed
|
|
400
|
+
-> bounded second-hypothesis soft retry when needed
|
|
399
401
|
-> body block deinterleaving
|
|
400
402
|
-> body GF(256) error/erasure correction
|
|
401
403
|
-> CRC-32 verification
|
|
@@ -409,7 +411,7 @@ Matrix decoding tries 0°, 90°, 180°, and 270° rotations.
|
|
|
409
411
|
|
|
410
412
|
## Compatibility
|
|
411
413
|
|
|
412
|
-
Format
|
|
414
|
+
Format v6 is intentionally incompatible with standard QR scanners and with the project's older ternary prototypes. The current encoder writes codeword cells using spectral-spatial placement and uses 5×5 nested alignment eyes at every scheduled alignment position. Format v5 used compact 3×3 secondary alignment markers while retaining the bottom-right primary marker at 5×5. The current decoder keeps a legacy Format v5 alignment-profile fallback, so existing v5 RGBW QuadQR symbols remain readable. The decoder also retains the pre-interleaver physical-order fallback for older RGBW matrices.
|
|
413
415
|
|
|
414
416
|
## Rendering profiles are not part of the wire format
|
|
415
417
|
|
|
@@ -417,11 +419,11 @@ The canonical QuadQR matrix is independent of presentation style. Renderers may
|
|
|
417
419
|
|
|
418
420
|
## Compression and signature flags
|
|
419
421
|
|
|
420
|
-
Format
|
|
422
|
+
Format v6 keeps the physical matrix and ECC framing unchanged while reserving two protected-header flags for optional internal payload metadata:
|
|
421
423
|
|
|
422
424
|
```text
|
|
423
425
|
bit 4 = internal payload-extension metadata present
|
|
424
426
|
bit 5 = signed-payload hint
|
|
425
427
|
```
|
|
426
428
|
|
|
427
|
-
These bits are not a public payload-type mode. Applications continue to encode normal UTF-8 text or arbitrary bytes. Compression and Ed25519 signing use a compact internal extension envelope only when metadata is required. The envelope, print profile, diagnostics, and scanability test model are specified in [`SPECIFICATION.md`](./SPECIFICATION.md). None of these features changes RGBW cell mapping or Spectrum ECC.
|
|
429
|
+
These bits are not a public payload-type mode. Applications continue to encode normal UTF-8 text or arbitrary bytes. Compression 3.0 (fast Auto, CPU-heavy Smart, explicit LZ/DEFLATE/Brotli levels with legacy LZ wire compatibility) and Ed25519 signing use a compact internal extension envelope only when metadata is required. The envelope, print profile, diagnostics, and scanability test model are specified in [`SPECIFICATION.md`](./SPECIFICATION.md). None of these features changes RGBW cell mapping or Spectrum ECC.
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://akanshsirohi.github.io/QuadQR/demo/"><strong>Try the Live Demo</strong></a>
|
|
13
13
|
·
|
|
14
|
-
<a href="https://akanshsirohi.github.io/QuadQR/
|
|
14
|
+
<a href="https://akanshsirohi.github.io/QuadQR/documentation/"><strong>Documentation Site</strong></a>
|
|
15
15
|
·
|
|
16
16
|
<a href="https://www.npmjs.com/package/quadqr-js">npm</a>
|
|
17
17
|
·
|
|
@@ -19,11 +19,9 @@
|
|
|
19
19
|
·
|
|
20
20
|
<a href="#getting-started">Use the Library</a>
|
|
21
21
|
·
|
|
22
|
-
<a href="
|
|
22
|
+
<a href="https://akanshsirohi.github.io/QuadQR/documentation/format.html">Format Reference</a>
|
|
23
23
|
·
|
|
24
|
-
<a href="
|
|
25
|
-
·
|
|
26
|
-
<a href="SPECIFICATION.md">Technical Specification</a>
|
|
24
|
+
<a href="https://akanshsirohi.github.io/QuadQR/documentation/specification.html">Technical Specification</a>
|
|
27
25
|
</p>
|
|
28
26
|
|
|
29
27
|
**QuadQR** is an experimental open-source 2D matrix code that uses RGBW color states instead of the two states used by a traditional black-and-white QR module. Normal mode stores **2 bits per RGBW data cell**. This experimental branch also includes an optional **High Density Mode**, implemented with Triangle16 split cells, that stores **4 raw bits per body cell**. High Density Mode is disabled by default.
|
|
@@ -59,7 +57,7 @@ const code = encodeText("High-density QuadQR", {
|
|
|
59
57
|
|
|
60
58
|
Image and camera scanning automatically detect High Density Mode, so a separate scanner mode is not required. High Density Mode is experimental and should be stress-tested at the intended physical size and camera distance.
|
|
61
59
|
|
|
62
|
-
See [
|
|
60
|
+
See the [High Density Mode documentation](https://akanshsirohi.github.io/QuadQR/documentation/high-density.html) for the physical cell mapping, protected-header strategy, scanner sampling rules, and reliability caveats.
|
|
63
61
|
|
|
64
62
|
QuadQR keeps the parts that make QR-like codes practical, such as a square matrix, finder patterns, timing structures, error correction, masking, perspective recovery, and camera scanning, while experimenting with a higher-density color-based data layer.
|
|
65
63
|
|
|
@@ -72,12 +70,12 @@ Below are QuadQR symbols generated with different error-correction profiles. The
|
|
|
72
70
|
<table>
|
|
73
71
|
<tr>
|
|
74
72
|
<td align="center">
|
|
75
|
-
<img src="assets/quadqr-
|
|
76
|
-
<sub><strong>
|
|
73
|
+
<img src="assets/quadqr-normal.png?raw=true" alt="QuadQR example using ECC profile L" width="320"><br>
|
|
74
|
+
<sub><strong>QuadQR</strong> · Normal (v3)</sub>
|
|
77
75
|
</td>
|
|
78
76
|
<td align="center">
|
|
79
|
-
<img src="assets/quadqr-
|
|
80
|
-
<sub><strong>
|
|
77
|
+
<img src="assets/quadqr-high-density.png?raw=true" alt="QuadQR example using ECC profile M" width="320"><br>
|
|
78
|
+
<sub><strong>QuadQR</strong> · Higher Data Density (v2)</sub>
|
|
81
79
|
</td>
|
|
82
80
|
</tr>
|
|
83
81
|
</table>
|
|
@@ -158,13 +156,13 @@ So at the raw data-cell level:
|
|
|
158
156
|
|
|
159
157
|
This is a **2× raw symbol-density advantage**.
|
|
160
158
|
|
|
161
|
-
### Spectrum ECC:
|
|
159
|
+
### Spectrum ECC 2.0: confidence-aware + soft decoding
|
|
162
160
|
|
|
163
|
-
QuadQR
|
|
161
|
+
QuadQR uses the fact that a color scanner knows more than only the winning state. For each sampled data cell it now retains the selected state, a confidence score, and a bounded second hypothesis. Triangle16 does the same after classifying both color regions.
|
|
164
162
|
|
|
165
|
-
|
|
163
|
+
The first recovery layer is still confidence-aware GF(256) Reed-Solomon: if normal hard-decision decoding fails, the least-confident byte symbols can be promoted to **known erasures**, allowing the existing parity budget to be spent more efficiently.
|
|
166
164
|
|
|
167
|
-
|
|
165
|
+
**Spectrum ECC 2.0** adds a bounded soft-decision fallback. If hard decoding and erasure decoding still fail, the decoder tries the second hypothesis for a small number of the least-confident data cells, first singly and then in tightly bounded pairs. Every candidate must still pass the normal Reed-Solomon checks and final CRC-32, so soft decoding does not relax integrity validation or add parity overhead.
|
|
168
166
|
|
|
169
167
|
QuadQR also applies a deterministic **spectral-spatial interleaver** after ECC. Neighboring logical codeword cells are scattered across distant physical data positions, so a scratch, glare patch, shadow, or localized print defect tends to affect many different RS symbols instead of destroying a contiguous run. The permutation is reversible and consumes **zero extra data cells**.
|
|
170
168
|
|
|
@@ -223,6 +221,24 @@ const compressed = encodeText("repeated repeated repeated", {
|
|
|
223
221
|
ecc: "M"
|
|
224
222
|
});
|
|
225
223
|
|
|
224
|
+
// Smart is CPU-heavy and only escalates when stronger compression can
|
|
225
|
+
// realistically reduce the physical QuadQR version.
|
|
226
|
+
const smart = encodeText(largeStructuredText, { compression: "smart" });
|
|
227
|
+
|
|
228
|
+
// Explicit codecs can choose their encoder level.
|
|
229
|
+
const lz = encodeText(largeStructuredText, {
|
|
230
|
+
compression: "lz",
|
|
231
|
+
compressionLevel: 9
|
|
232
|
+
});
|
|
233
|
+
const brotli = encodeText("hello ".repeat(1000), {
|
|
234
|
+
compression: "brotli",
|
|
235
|
+
compressionLevel: 11
|
|
236
|
+
});
|
|
237
|
+
const deflate = encodeText(largeStructuredText, {
|
|
238
|
+
compression: "deflate",
|
|
239
|
+
compressionLevel: 9
|
|
240
|
+
});
|
|
241
|
+
|
|
226
242
|
const keys = await generateSigningKeyPair();
|
|
227
243
|
const signed = await encodeSignedText("verified offline", {
|
|
228
244
|
compression: "auto",
|
|
@@ -238,7 +254,7 @@ console.log(verified.signatureVerified); // true
|
|
|
238
254
|
console.log(verified.signatureTrusted); // true
|
|
239
255
|
```
|
|
240
256
|
|
|
241
|
-
Compression modes are `none`, `auto`, and `lz`. `auto` keeps the
|
|
257
|
+
Compression modes are `none`, `auto`, `smart`, `brotli`, `deflate`, and `lz`. `auto` is the fast default: it compares LZ level 6, DEFLATE level 6, and Brotli quality 6 once, including envelope overhead, and keeps the smallest final representation. `smart` is an opt-in CPU-heavy mode. It starts with the same balanced pass, checks the resulting QuadQR version, and only escalates to DEFLATE 8 / Brotli 9 and then DEFLATE 9 / Brotli 11 when a smaller physical version is realistically reachable; LZ stays at its default level in Auto/Smart. Explicit `lz` accepts `compressionLevel: 1..9` with default 6, explicit `deflate` accepts `1..9` with default 6, and explicit `brotli` accepts `0..11` with default 11. Compression level is an encoder-only setting and is not stored in the symbol because the decoder does not need it. All codecs are synchronous and bundled with QuadQR, so the same compression path works in browsers and server-side Node.js without `node:zlib`, `CompressionStream`, or a runtime dependency. The demo keeps these CPU-heavy operations in module Web Workers so the browser UI remains responsive. See the [compression documentation](https://akanshsirohi.github.io/QuadQR/documentation/compression.html) for the exact Smart escalation policy and level API. Ed25519 signing stores the signature plus an optional compact `keyId`; the public verification key stays outside the QuadQR by default. Applications do not need to choose or maintain content types.
|
|
242
258
|
|
|
243
259
|
Signing can also be composed with Secure Payload. QuadQR compresses if requested, signs the normal payload with the private key, then encrypts the protected bytes with AES-256-GCM. A verifier supplies the trusted public key separately, or resolves it from `keyId`.
|
|
244
260
|
|
|
@@ -250,7 +266,7 @@ Scanner results include normalized diagnostics such as `confidence`, `geometryCo
|
|
|
250
266
|
|
|
251
267
|
For regression and demo testing, `runImageStressTest()` / `assessScanability()` apply deterministic blur, brightness, exposure, shadow, contrast, perspective, JPEG-like artifacts, and downscaling. The browser demo exposes the same tools as an interactive stress-test lab and shows an overall scanability rating.
|
|
252
268
|
|
|
253
|
-
The interoperability details are
|
|
269
|
+
The interoperability details are covered in the [technical specification](https://akanshsirohi.github.io/QuadQR/documentation/specification.html).
|
|
254
270
|
|
|
255
271
|
---
|
|
256
272
|
|
|
@@ -424,7 +440,7 @@ QuadQR currently uses:
|
|
|
424
440
|
- square modules;
|
|
425
441
|
- three 7×7 black-and-white finder patterns;
|
|
426
442
|
- black-and-white timing structures;
|
|
427
|
-
-
|
|
443
|
+
- distributed 5×5 nested alignment eyes on versions that use alignment patterns;
|
|
428
444
|
- RGB calibration swatches;
|
|
429
445
|
- structural black/white references;
|
|
430
446
|
- a two-column zig-zag physical data-position path;
|
|
@@ -437,7 +453,7 @@ QuadQR currently uses:
|
|
|
437
453
|
|
|
438
454
|
White is a valid data state.
|
|
439
455
|
|
|
440
|
-
QuadQR still uses exactly three large finder patterns, just like standard QR. Starting at version 2, alignment markers follow the standard QR version-dependent center schedule
|
|
456
|
+
QuadQR still uses exactly three large finder patterns, just like standard QR. Starting at version 2, Format v6 alignment markers follow the standard QR version-dependent center schedule and every scheduled alignment marker is a full 5×5 nested black/white/black eye. These distributed references give the detector stronger anchors for high-version and projectively distorted symbols. Version 1 keeps one QuadQR-specific 5×5 bottom-right bootstrap marker because it otherwise would have no fourth projective reference. The decoder retains the compact 3×3 secondary-marker profile used by Format v5 so existing v5 symbols remain readable.
|
|
441
457
|
|
|
442
458
|
The decoder does not treat a white-looking area as automatically empty. It reconstructs the matrix geometry first and then determines whether a sampled position is structural or data.
|
|
443
459
|
|
|
@@ -465,7 +481,7 @@ White → (255, 255, 255)
|
|
|
465
481
|
|
|
466
482
|
Real camera input is not expected to match those exact values.
|
|
467
483
|
|
|
468
|
-
QuadQR includes calibration and nearest-color classification so the scanner can work with observed colors after lighting, camera processing, perspective changes, and other image transformations. The clean-frame path
|
|
484
|
+
QuadQR includes calibration and nearest-color classification so the scanner can work with observed colors after lighting, camera processing, perspective changes, and other image transformations. The clean-frame path starts with a streaming 1:1:3:1:1 finder detector on the RGB value channel, direct cross-checks, local-threshold fallback, and directional module-size/version estimation. Once the three finder eyes form a valid geometry, QuadQR immediately tries the highest-ranked perspective candidate instead of waiting for every lower-ranked geometry. Near-front-facing symbols can use a three-finder affine fast path, while projectively distorted symbols continue through the full homography/alignment solver. Format v6 distributed 5×5 alignment eyes provide stronger local perspective anchors, and a cheap nested-eye locator is tried before the broader alignment search. If a steep angle leaves exactly two strong finder patterns, the bounded looser third-finder pass still runs before heavier color recovery. Only after geometry/color decoding still fails does QuadQR progressively try the existing stronger recovery, including white balancing, a 3×4 affine color-calibration model learned from the known black/white/R/G/B references, spatial normalization, Auto Tone / Auto Contrast / QuadQR Auto Color-style enhancement, soft-decision ECC, and bounded sub-module geometry refinement. For live video, normal detection works from the CSS-visible `object-fit: cover` camera region at a 640 px working dimension. A bounded 960 px retry is reserved for difficult dense frames that already show useful finder evidence, rather than being part of the ordinary camera loop. If finder geometry is already strong but color decoding fails, a QR-only rectified pixel enhancement retry is performed immediately; whole-frame enhancement remains reserved for harder locator failures.
|
|
469
485
|
|
|
470
486
|
---
|
|
471
487
|
|
|
@@ -537,17 +553,17 @@ module-grid reconstruction
|
|
|
537
553
|
↓
|
|
538
554
|
fast observed-RGB decode attempt
|
|
539
555
|
↓ (only if needed)
|
|
540
|
-
white balance + spatial normalization
|
|
556
|
+
white balance + affine cross-channel calibration + spatial normalization
|
|
541
557
|
↓ (only if still needed)
|
|
542
|
-
Auto Tone / Auto Contrast / Auto Color-style recovery
|
|
558
|
+
Auto Tone / Auto Contrast / QuadQR Auto Color-style recovery
|
|
543
559
|
↓ (only if still needed)
|
|
544
560
|
sub-module geometry refinement
|
|
545
561
|
↓
|
|
546
562
|
RGB + structural black/white calibration
|
|
547
563
|
↓
|
|
548
|
-
|
|
564
|
+
RGBW/Triangle16 classification + confidence + second hypothesis
|
|
549
565
|
↓
|
|
550
|
-
|
|
566
|
+
unmasking
|
|
551
567
|
↓
|
|
552
568
|
reverse spectral-spatial permutation
|
|
553
569
|
↓
|
|
@@ -555,6 +571,8 @@ protected header Reed-Solomon hard decode
|
|
|
555
571
|
↓
|
|
556
572
|
confidence-guided erasure retry when needed
|
|
557
573
|
↓
|
|
574
|
+
bounded Spectrum ECC 2.0 soft-hypothesis retry when needed
|
|
575
|
+
↓
|
|
558
576
|
body deinterleaving + error/erasure Reed-Solomon decode
|
|
559
577
|
↓
|
|
560
578
|
CRC-32 verification
|
|
@@ -644,7 +662,7 @@ Logo overlays intentionally consume some ECC margin because they cover encoded c
|
|
|
644
662
|
|
|
645
663
|
**Live demo:** https://akanshsirohi.github.io/QuadQR/demo/
|
|
646
664
|
|
|
647
|
-
The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded. The generator keeps only payload, version, and ECC visible by default; optional capabilities are grouped into independent advanced accordions.
|
|
665
|
+
The browser demo runs directly on GitHub Pages and is split into separate views so the interface does not become overloaded. The generator keeps only payload, version, and ECC visible by default; optional capabilities are grouped into independent advanced accordions. CPU-heavy encoding/compression, rendered-image verification, uploaded-image scanning, scanability tests, Reliability Lab runs, perspective sweeps, and codec benchmarks are dispatched to module Web Workers. Generation shows explicit progress instead of freezing the browser, repeated UI changes are coalesced, and render-only changes reuse the already encoded matrix instead of recompressing the payload.
|
|
648
666
|
|
|
649
667
|
### Generator & Image Scanner
|
|
650
668
|
|
|
@@ -764,7 +782,7 @@ console.log(result.text);
|
|
|
764
782
|
The `quadqr-js` package can be loaded directly from npm-backed CDNs:
|
|
765
783
|
|
|
766
784
|
```html
|
|
767
|
-
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.1
|
|
785
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.5.1/dist/quadqr.min.js"></script>
|
|
768
786
|
<script>
|
|
769
787
|
const code = QuadQR.encodeText("Hello from a script tag");
|
|
770
788
|
</script>
|
|
@@ -798,7 +816,7 @@ npx quadqr-js encode "Private data" --password "my-password" -o secure.png
|
|
|
798
816
|
npx quadqr-js decode secure.png --password "my-password"
|
|
799
817
|
```
|
|
800
818
|
|
|
801
|
-
See [
|
|
819
|
+
See the [CLI reference](https://akanshsirohi.github.io/QuadQR/documentation/cli.html) for all CLI options, including compression, raw 256-bit key mode, signing, print mode, and scanner diagnostics.
|
|
802
820
|
|
|
803
821
|
### Run from source
|
|
804
822
|
|
|
@@ -820,6 +838,16 @@ Build the distributable browser, Node.js, CDN, and WASM files:
|
|
|
820
838
|
npm run build
|
|
821
839
|
```
|
|
822
840
|
|
|
841
|
+
`npm run build` verifies the checked-in WASM binary against `wasm-src/quadqr_core.c`, the WASM compiler flags, and the recorded binary hash. If everything matches, the verified prebuilt binary is reused. If the C source or build flags changed, the build automatically recompiles WASM with LLVM/Clang. If WASM is stale and `clang` is unavailable, the build fails instead of silently shipping an old binary.
|
|
842
|
+
|
|
843
|
+
To force only the WASM rebuild:
|
|
844
|
+
|
|
845
|
+
```bash
|
|
846
|
+
npm run build:wasm
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
A forced WASM rebuild requires `clang` in `PATH`. No Rust, Cargo, or Emscripten toolchain is required. The build records its verification data in `wasm/quadqr-core.build.json`.
|
|
850
|
+
|
|
823
851
|
Start the interactive demo:
|
|
824
852
|
|
|
825
853
|
```bash
|
|
@@ -840,7 +868,7 @@ npm run benchmark
|
|
|
840
868
|
|
|
841
869
|
### Optional WASM acceleration
|
|
842
870
|
|
|
843
|
-
The package ships a prebuilt WASM helper but never requires it.
|
|
871
|
+
The package ships a prebuilt WASM helper but never requires it. It accelerates CRC-32 plus the scanner's RGBA-to-grayscale, Otsu-threshold, and binary finder preprocessing hot path.
|
|
844
872
|
|
|
845
873
|
```js
|
|
846
874
|
import { initWasm } from "quadqr-js";
|
|
@@ -848,7 +876,7 @@ import { initWasm } from "quadqr-js";
|
|
|
848
876
|
await initWasm();
|
|
849
877
|
```
|
|
850
878
|
|
|
851
|
-
If WASM cannot load, the normal JavaScript codec remains available.
|
|
879
|
+
If WASM cannot load, the normal JavaScript codec/scanner remains available. For camera or repeated image scanning, initialize WASM once during application startup.
|
|
852
880
|
|
|
853
881
|
---
|
|
854
882
|
|
|
@@ -883,7 +911,8 @@ wasm-src/
|
|
|
883
911
|
quadqr_core.c Small portable WASM accelerator source
|
|
884
912
|
|
|
885
913
|
wasm/
|
|
886
|
-
quadqr-core.wasm
|
|
914
|
+
quadqr-core.wasm Source-tree WASM build output
|
|
915
|
+
quadqr-core.build.json Source/build/binary verification metadata
|
|
887
916
|
|
|
888
917
|
dist/
|
|
889
918
|
index.js ESM package entry
|
|
@@ -896,25 +925,29 @@ dist/
|
|
|
896
925
|
|
|
897
926
|
demo/
|
|
898
927
|
index.html Interactive generator, image scanner, camera scanner, benchmark
|
|
899
|
-
app.js
|
|
928
|
+
app.js Responsive UI + worker orchestration
|
|
929
|
+
compute-worker.js Background encode/scan/reliability/benchmark tasks
|
|
900
930
|
styles.css
|
|
901
931
|
|
|
902
|
-
|
|
903
|
-
index.html
|
|
904
|
-
|
|
905
|
-
|
|
932
|
+
documentation/
|
|
933
|
+
index.html Documentation home
|
|
934
|
+
getting-started.html
|
|
935
|
+
browser.html
|
|
936
|
+
node.html
|
|
937
|
+
scanning.html
|
|
938
|
+
compression.html
|
|
939
|
+
security.html
|
|
940
|
+
high-density.html
|
|
941
|
+
reliability.html
|
|
942
|
+
api.html
|
|
943
|
+
cli.html
|
|
944
|
+
wasm.html
|
|
945
|
+
format.html
|
|
946
|
+
specification.html
|
|
947
|
+
app.js Navigation, search, copy, and theme behavior
|
|
948
|
+
search-index.js Client-side documentation search index
|
|
949
|
+
styles.css Responsive documentation UI
|
|
906
950
|
|
|
907
|
-
docs/
|
|
908
|
-
README.md Markdown documentation index
|
|
909
|
-
GETTING_STARTED.md
|
|
910
|
-
API.md
|
|
911
|
-
BROWSER_CDN.md
|
|
912
|
-
NODE.md
|
|
913
|
-
SECURITY.md
|
|
914
|
-
CLI.md
|
|
915
|
-
WASM.md
|
|
916
|
-
|
|
917
|
-
types/ TypeScript declarations for JavaScript consumers
|
|
918
951
|
bin/ `quadqr` CLI (`npx quadqr-js`)
|
|
919
952
|
scripts/ Build, benchmark, and local server scripts
|
|
920
953
|
tests/ Codec and package distribution tests
|
|
@@ -1059,7 +1092,7 @@ Returns a standalone SVG string using the same exact `imageSize`, render styles,
|
|
|
1059
1092
|
|
|
1060
1093
|
### `scanImageData(imageData, options?)`
|
|
1061
1094
|
|
|
1062
|
-
Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Dense versions use distributed alignment markers to refine a plausible but imperfect projective solution, and a two-finder recovery pass can rescue a third locator that has been stretched by perspective. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
|
|
1095
|
+
Runs the complete perspective-aware and color-aware image scanner. The scanner first tries the normal detected geometry with the observed RGB palette, preserving the fast path for clean images. Dense versions use distributed alignment markers to refine a plausible but imperfect projective solution, and a two-finder recovery pass can rescue a third locator that has been stretched by perspective. Only after that fails does it progressively fall back to per-channel white balancing, spatial black/white normalization, tighter centre sampling, a cheap module-grid Auto Tone / Auto Contrast / QuadQR Auto Color-style recovery, a rectified QR-region pixel enhancement pass, and finally bounded sub-module geometry micro-refinement. If locator detection itself is weakened by a flat/yellow frame, a full-image enhancement retry is also available. RGBW confidence values are carried into Reed-Solomon so ambiguous cells can be treated as erasures when ordinary hard-decision ECC is insufficient.
|
|
1063
1096
|
|
|
1064
1097
|
### `scanFile(file, options?)`
|
|
1065
1098
|
|
|
@@ -1071,7 +1104,7 @@ Scans one frame from an HTML video element. By default, if the video is displaye
|
|
|
1071
1104
|
|
|
1072
1105
|
### `startCameraScanner(video, options?)`
|
|
1073
1106
|
|
|
1074
|
-
Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop.
|
|
1107
|
+
Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. Modern browsers use a **dual-worker camera engine**: a lightweight fresh-frame worker continuously runs normal finder/geometry/decode attempts, while an independent recovery worker retains the complete high-resolution, Auto Color, precise-alignment, perspective, multi-frame, ECC, and damaged-code recovery stack. A slow recovery attempt therefore cannot prevent the fast worker from inspecting a newer camera frame. Finder detection remains JavaScript; optional WASM accelerates grayscale/binary preprocessing and CRC beneath the same detector. The scheduler uses `requestVideoFrameCallback()` when available and does not queue stale fast-path frames. Normal camera acquisition requests an environment camera around 1280×720 and crops/resizes the visible preview to a 640 px working bitmap **before** transferring it to the worker. Once a candidate validates structure, Spectrum ECC, and CRC, scanning returns immediately. If the fast worker misses, full recovery runs concurrently on a fresh frame at up to 960 px. Strong finder evidence dispatches recovery quickly; finder-less frames still receive periodic full recovery so severe color casts or damaged locators retain the same rescue paths. QuadQR Auto Color crop profiles, center-weighted histograms, threshold bracketing, precise alignment, projective recovery, QR-region enhancement, multi-frame confidence fusion, and soft-decision Spectrum ECC are unchanged. `cameraHighResolutionMaxDimension` defaults to 960. The optional `onDiagnostic(event)` callback exposes whether an event came from the fast or recovery worker, finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact frame that decoded, including enhanced recovery pixels when applicable, so UIs can keep the frozen frame and finder overlay aligned.
|
|
1075
1108
|
|
|
1076
1109
|
### `getVersionInfo(version, options?)`
|
|
1077
1110
|
|
|
@@ -1112,7 +1145,10 @@ The current test suite covers areas including:
|
|
|
1112
1145
|
- color-cast scanning;
|
|
1113
1146
|
- dirty-camera stress scanning with strong yellow cast, haze, blue-channel suppression, and blur;
|
|
1114
1147
|
- low-contrast warm-camera regression where normal scanning fails but progressive Auto Tone / Contrast / Color recovery succeeds;
|
|
1115
|
-
- multi-frame
|
|
1148
|
+
- multi-frame confidence fusion and tracked-symbol consistency;
|
|
1149
|
+
- Spectrum ECC 2.0 bounded soft-decision recovery;
|
|
1150
|
+
- affine cross-channel color calibration;
|
|
1151
|
+
- multi-point Triangle16 region sampling with instability-aware confidence;
|
|
1116
1152
|
- benchmark reference data;
|
|
1117
1153
|
- timed codec round trips;
|
|
1118
1154
|
- password-mode secure round trips and wrong-password rejection;
|
package/SPECIFICATION.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
QuadQR is an experimental RGBW matrix symbology. Normal mode uses 4-state RGBW cells, while the optional **High Density Mode** is experimental and uses the 16-state Triangle16 physical layout. It is not ISO/IEC QR Code and is not intended to be decoded by standard QR readers.
|
|
6
6
|
|
|
7
|
-
The physical matrix format remains **QuadQR Format
|
|
7
|
+
The physical matrix format remains **QuadQR Format v6**. Normal application data is always treated simply as UTF-8 text or arbitrary bytes. Compression, signatures, encryption, rendering, and diagnostics are optional features layered around that stable matrix codec.
|
|
8
8
|
|
|
9
9
|
For exact matrix geometry and Reed-Solomon framing, see [`FORMAT.md`](./FORMAT.md).
|
|
10
10
|
|
|
@@ -17,7 +17,7 @@ Application bytes / UTF-8 text
|
|
|
17
17
|
├─ optional Ed25519 signature metadata (internal)
|
|
18
18
|
├─ optional Secure Payload v1 (AES-256-GCM)
|
|
19
19
|
│
|
|
20
|
-
└─ QuadQR Format
|
|
20
|
+
└─ QuadQR Format v6
|
|
21
21
|
├─ protected header
|
|
22
22
|
├─ CRC-32
|
|
23
23
|
├─ GF(256) Reed-Solomon Spectrum ECC
|
|
@@ -42,7 +42,7 @@ Structural black is separate from the data alphabet. One encoded byte maps to ex
|
|
|
42
42
|
|
|
43
43
|
### High Density Mode
|
|
44
44
|
|
|
45
|
-
When `highDensity: true` is selected, each payload module has a fixed `/` diagonal and two independently classified RGBW regions. This creates 16 states and 4 raw bits per body cell. The protected header stays solid-color at 2 bits per cell for robust bootstrap recovery, then header flag bit 6 tells the decoder that the ECC/body stream uses Triangle16 packing. Scanner sampling uses
|
|
45
|
+
When `highDensity: true` is selected, each payload module has a fixed `/` diagonal and two independently classified RGBW regions. This creates 16 states and 4 raw bits per body cell. The protected header stays solid-color at 2 bits per cell for robust bootstrap recovery, then header flag bit 6 tells the decoder that the ECC/body stream uses Triangle16 packing. Scanner sampling uses three interior anchors per triangle, robust aggregation, and a spatial-instability penalty while excluding the diagonal boundary.
|
|
46
46
|
|
|
47
47
|
## 3. Matrix sizing
|
|
48
48
|
|
|
@@ -53,9 +53,9 @@ version = 1..40
|
|
|
53
53
|
|
|
54
54
|
Three 7×7 finder patterns are always present. Version 1 has the legacy QuadQR 5×5 bottom-right alignment marker. Versions 2–40 use the distributed alignment schedule defined in `FORMAT.md`.
|
|
55
55
|
|
|
56
|
-
## 4. Format
|
|
56
|
+
## 4. Format v6 header flags
|
|
57
57
|
|
|
58
|
-
The protected Format
|
|
58
|
+
The protected Format v6 header uses:
|
|
59
59
|
|
|
60
60
|
```text
|
|
61
61
|
bit 0 UTF-8 text flag
|
|
@@ -87,7 +87,7 @@ Version 2+ parity profiles:
|
|
|
87
87
|
| Q | 36 | 18 |
|
|
88
88
|
| H | 48 | 24 |
|
|
89
89
|
|
|
90
|
-
The decoder retains per-cell color confidence. Low-confidence bytes can be promoted to known erasures, allowing Reed-Solomon recovery to use the existing parity budget more efficiently.
|
|
90
|
+
The decoder retains per-cell color confidence and one bounded alternate hypothesis from image/camera classification. Low-confidence bytes can first be promoted to known erasures, allowing Reed-Solomon recovery to use the existing parity budget more efficiently. If hard and error/erasure decoding still fail, Spectrum ECC 2.0 may try the alternate state for a bounded set of the least-confident cells. Candidate search is limited to single substitutions and a small pair set by default, and every successful path must still satisfy Reed-Solomon verification and CRC-32.
|
|
91
91
|
|
|
92
92
|
## 6. Internal payload extension envelope
|
|
93
93
|
|
|
@@ -104,12 +104,12 @@ Fixed header size: **16 bytes**.
|
|
|
104
104
|
| Offset | Size | Field |
|
|
105
105
|
|---:|---:|---|
|
|
106
106
|
| 0 | 4 | ASCII magic `QPX1` |
|
|
107
|
-
| 4 | 1 | Extension version (`
|
|
107
|
+
| 4 | 1 | Extension version (`3`; decoder also accepts legacy `1` and `2`) |
|
|
108
108
|
| 5 | 1 | Flags |
|
|
109
109
|
| 6 | 1 | Compression ID |
|
|
110
110
|
| 7 | 1 | Signature algorithm ID |
|
|
111
111
|
| 8 | 4 | Original application payload length, big-endian |
|
|
112
|
-
| 12 | 1 | Signing key-ID length (v2); legacy signer-label length in v1 |
|
|
112
|
+
| 12 | 1 | Signing key-ID length (v2+); legacy signer-label length in v1 |
|
|
113
113
|
| 13 | 1 | Optional embedded public-key length |
|
|
114
114
|
| 14 | 1 | Signature length |
|
|
115
115
|
| 15 | 1 | Reserved (`0`) |
|
|
@@ -128,7 +128,7 @@ bit 1 compressed
|
|
|
128
128
|
bit 2 public key embedded
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
The envelope contains no semantic content type. The Format
|
|
131
|
+
The envelope contains no semantic content type. The Format v6 text flag still determines whether the recovered application payload should be decoded as UTF-8 text.
|
|
132
132
|
|
|
133
133
|
## 7. Compression
|
|
134
134
|
|
|
@@ -136,33 +136,48 @@ Compression IDs:
|
|
|
136
136
|
|
|
137
137
|
```text
|
|
138
138
|
0 = none
|
|
139
|
-
1 = QuadQR portable LZ
|
|
139
|
+
1 = QuadQR portable LZ (legacy)
|
|
140
|
+
2 = QuadQR portable raw DEFLATE
|
|
141
|
+
3 = bundled Brotli
|
|
140
142
|
```
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
Compression ID `1` is the original LZSS-style stream with groups of up to eight tokens. Each group begins with one flag byte. A flag bit of `0` means a one-byte literal. A flag bit of `1` means a two-byte back-reference:
|
|
143
145
|
|
|
144
146
|
```text
|
|
145
147
|
12-bit offset: 1..4095 bytes
|
|
146
148
|
4-bit length: stored value + 3, therefore 3..18 bytes
|
|
147
149
|
```
|
|
148
150
|
|
|
151
|
+
Compression ID `1` keeps the original QuadQR LZSS-style stream. Encoder levels `1..9` control candidate-history depth and bounded lazy-match effort; level 6 preserves the historical 32-candidate search depth. The 4095-byte window, 3..18 byte back-reference format, and decoder remain unchanged, and the level is not serialized.
|
|
152
|
+
|
|
153
|
+
Compression ID `2` is a raw RFC 1951 DEFLATE stream. QuadQR's portable encoder uses a deterministic fixed-Huffman block, a 32 KiB LZ77 window, distances up to 32768 bytes, and matches up to 258 bytes. Encoder levels `1..9` control candidate-chain depth and lazy-match effort; the level is not serialized and does not change decoder behavior. The QuadQR decoder accepts the stored/fixed block forms emitted by the library.
|
|
154
|
+
|
|
155
|
+
Compression ID `3` is a standard Brotli stream produced and decoded by QuadQR's bundled synchronous JavaScript codec. It uses no Node `zlib`, browser `CompressionStream`, DOM API, native addon, or network-loaded codec at runtime. Brotli is especially effective for repetitive text and structured payloads, while DEFLATE and legacy LZ remain available for deterministic selection and backward compatibility.
|
|
156
|
+
|
|
149
157
|
Public compression modes are:
|
|
150
158
|
|
|
151
159
|
```text
|
|
152
160
|
none
|
|
153
161
|
auto
|
|
162
|
+
smart
|
|
163
|
+
brotli
|
|
164
|
+
deflate
|
|
154
165
|
lz
|
|
155
166
|
```
|
|
156
167
|
|
|
157
|
-
`compression: "auto"`
|
|
168
|
+
`compression: "auto"` is the fast balanced policy. It evaluates LZ level 6, DEFLATE level 6, and Brotli quality 6 exactly once and chooses the smallest complete stored representation. For unsigned payloads that comparison includes the 16-byte extension-envelope cost, so Auto remains zero-overhead when compression is not useful.
|
|
169
|
+
|
|
170
|
+
`compression: "smart"` is the opt-in CPU-heavy policy. It begins with the Auto candidates, computes the resulting Format v6 QuadQR version using the selected ECC/profile/version bounds, and only escalates when the next smaller matrix is plausibly reachable. The strong stage tests DEFLATE 8 and Brotli 9. If the symbol remains close to a smaller-version boundary, the maximum stage tests DEFLATE 9 and Brotli 11. With an explicitly requested fixed version, Smart does not chase a smaller matrix, but it may escalate when the balanced result does not fit and stronger compression can plausibly make that requested version fit. Signed and secure pipelines include their fixed envelope overhead when evaluating those version boundaries.
|
|
171
|
+
|
|
172
|
+
Explicit `compression: "lz"` and `compression: "deflate"` accept `compressionLevel` `1..9` and default to 6. Explicit `compression: "brotli"` accepts `compressionLevel` `0..11` and defaults to 11. Compression levels are encoder-only parameters and are intentionally not stored in the extension envelope because LZ, RFC 1951, and Brotli decoders do not require them. Auto and Smart use LZ level 6; Smart's staged escalation remains focused on DEFLATE/Brotli.
|
|
158
173
|
|
|
159
|
-
Compression occurs before signing, encryption, and Format
|
|
174
|
+
Compression occurs before signing, encryption, and Format v6 ECC.
|
|
160
175
|
|
|
161
176
|
## 8. Signed QuadQR
|
|
162
177
|
|
|
163
178
|
Signature algorithm ID `1` is **Ed25519**.
|
|
164
179
|
|
|
165
|
-
Signed payloads in extension v2 store:
|
|
180
|
+
Signed payloads in extension v2/v3 store:
|
|
166
181
|
|
|
167
182
|
```text
|
|
168
183
|
optional compact key ID
|
|
@@ -196,13 +211,13 @@ application payload
|
|
|
196
211
|
→ optional compression
|
|
197
212
|
→ optional Ed25519 signature metadata
|
|
198
213
|
→ AES-256-GCM Secure Payload v1
|
|
199
|
-
→ Format
|
|
214
|
+
→ Format v6 ECC/matrix
|
|
200
215
|
```
|
|
201
216
|
|
|
202
217
|
After scanning:
|
|
203
218
|
|
|
204
219
|
```text
|
|
205
|
-
Format
|
|
220
|
+
Format v6 decode
|
|
206
221
|
→ AES-GCM authentication/decryption
|
|
207
222
|
→ internal compression/signature metadata processing
|
|
208
223
|
→ application payload
|
|
@@ -249,7 +264,7 @@ Recommended general-purpose starting module size is **0.40 mm/module**. Real pri
|
|
|
249
264
|
|
|
250
265
|
## 12. Logo safety
|
|
251
266
|
|
|
252
|
-
Logos are rendering overlays and never modify Format
|
|
267
|
+
Logos are rendering overlays and never modify Format v6 data structures.
|
|
253
268
|
|
|
254
269
|
`size: "auto"` estimates a conservative logo ratio from:
|
|
255
270
|
|
|
@@ -305,7 +320,7 @@ These scores are regression/testing aids. They do not replace validation with re
|
|
|
305
320
|
|
|
306
321
|
## 15. Capacity planning
|
|
307
322
|
|
|
308
|
-
Capacity is determined from the actual Format
|
|
323
|
+
Capacity is determined from the actual Format v6 layout, protected header, CRC, and Spectrum ECC plan. Compression can reduce stored payload bytes, while signatures and encryption add metadata bytes before the matrix codec.
|
|
309
324
|
|
|
310
325
|
The benchmark helper can report:
|
|
311
326
|
|
|
@@ -325,7 +340,7 @@ Implementations should follow these rules:
|
|
|
325
340
|
|
|
326
341
|
1. Keep RGBW mapping exactly `R=00, G=01, B=10, W=11`.
|
|
327
342
|
2. Keep GF(256) Spectrum ECC and its errors+erasures behavior.
|
|
328
|
-
3. Preserve Format v5 decoding
|
|
343
|
+
3. Preserve Format v5 decoding as a legacy compatibility path while encoding new symbols as Format v6.
|
|
329
344
|
4. Keep application semantics outside the QuadQR codec. Do not require a growing content-type registry.
|
|
330
345
|
5. Treat compression/signature metadata as internal transport metadata, not a separate user payload mode.
|
|
331
346
|
6. Treat `keyId` only as an identifier. Signer trust comes from an external trusted public-key binding.
|