@sythos/js_barcode_universal 1.5.8 → 1.5.10

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.
Files changed (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +61 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -27,7 +27,6 @@
27
27
  *
28
28
  * Original work. No code from any other barcode implementation.
29
29
  */
30
-
31
30
  /**
32
31
  * QR Code detection — finding symbols in a binarized image.
33
32
  *
@@ -50,22 +49,17 @@
50
49
  *
51
50
  * @module qr/detector
52
51
  */
53
-
54
52
  import { NotFoundError } from '../core/errors.js';
55
53
  import { PerspectiveTransform } from '../image/perspective.js';
56
54
  import { sampleQuad } from '../image/grid-sampler.js';
57
55
  import { decodeQR } from './decoder.js';
58
-
59
56
  /** Finder pattern run ratios, centre run first in the array's own order. */
60
57
  const FINDER_RATIOS = [1, 1, 3, 1, 1];
61
-
62
58
  /** Alignment pattern run ratios. */
63
59
  const ALIGNMENT_RATIOS = [1, 1, 1, 1, 1];
64
-
65
60
  /** Smallest and largest legal symbol dimensions, in modules. */
66
61
  const MIN_DIMENSION = 21;
67
62
  const MAX_DIMENSION = 177;
68
-
69
63
  /**
70
64
  * Do five alternating runs match the expected ratios?
71
65
  *
@@ -78,23 +72,24 @@ const MAX_DIMENSION = 177;
78
72
  * @returns {number} The implied module size, or 0 if the ratios do not match.
79
73
  */
80
74
  function matchRatios(counts, ratios) {
81
- let total = 0;
82
- let units = 0;
83
- for (let i = 0; i < 5; i++) {
84
- if (counts[i] === 0) return 0;
85
- total += counts[i];
86
- units += ratios[i];
87
- }
88
- if (total < units) return 0;
89
-
90
- const moduleSize = total / units;
91
- const tolerance = moduleSize / 2;
92
- for (let i = 0; i < 5; i++) {
93
- if (Math.abs(counts[i] - moduleSize * ratios[i]) > tolerance * ratios[i]) return 0;
94
- }
95
- return moduleSize;
75
+ let total = 0;
76
+ let units = 0;
77
+ for (let i = 0; i < 5; i++) {
78
+ if (counts[i] === 0)
79
+ return 0;
80
+ total += counts[i];
81
+ units += ratios[i];
82
+ }
83
+ if (total < units)
84
+ return 0;
85
+ const moduleSize = total / units;
86
+ const tolerance = moduleSize / 2;
87
+ for (let i = 0; i < 5; i++) {
88
+ if (Math.abs(counts[i] - moduleSize * ratios[i]) > tolerance * ratios[i])
89
+ return 0;
90
+ }
91
+ return moduleSize;
96
92
  }
97
-
98
93
  /**
99
94
  * Scan one row for the finder run ratio.
100
95
  *
@@ -104,52 +99,45 @@ function matchRatios(counts, ratios) {
104
99
  * @param {(centreX: number, moduleSize: number) => void} onHit
105
100
  */
106
101
  function scanRow(image, y, ratios, onHit) {
107
- const width = image.width;
108
- const counts = [0, 0, 0, 0, 0];
109
- let state = 0;
110
-
111
- for (let x = 0; x < width; x++) {
112
- const dark = image.get(x, y);
113
-
114
- // Even states are dark runs, odd states light.
115
- if (dark === ((state & 1) === 0)) {
116
- counts[state]++;
117
- continue;
118
- }
119
-
120
- // A leading light margin is not part of any pattern.
121
- if (state === 0 && counts[0] === 0) continue;
122
-
123
- if (state < 4) {
124
- state++;
125
- counts[state] = 1;
126
- continue;
127
- }
128
-
129
- // Five runs complete, and the sixth has begun.
130
- const moduleSize = matchRatios(counts, ratios);
131
- if (moduleSize > 0) {
132
- onHit(x - counts[4] - counts[3] - counts[2] / 2, moduleSize);
102
+ const width = image.width;
103
+ const counts = [0, 0, 0, 0, 0];
104
+ let state = 0;
105
+ for (let x = 0; x < width; x++) {
106
+ const dark = image.get(x, y);
107
+ // Even states are dark runs, odd states light.
108
+ if (dark === ((state & 1) === 0)) {
109
+ counts[state]++;
110
+ continue;
111
+ }
112
+ // A leading light margin is not part of any pattern.
113
+ if (state === 0 && counts[0] === 0)
114
+ continue;
115
+ if (state < 4) {
116
+ state++;
117
+ counts[state] = 1;
118
+ continue;
119
+ }
120
+ // Five runs complete, and the sixth has begun.
121
+ const moduleSize = matchRatios(counts, ratios);
122
+ if (moduleSize > 0) {
123
+ onHit(x - counts[4] - counts[3] - counts[2] / 2, moduleSize);
124
+ }
125
+ // Slide the window on by two runs: the trailing dark run of a rejected
126
+ // candidate is often the leading dark run of the real one.
127
+ counts[0] = counts[2];
128
+ counts[1] = counts[3];
129
+ counts[2] = counts[4];
130
+ counts[3] = 1;
131
+ counts[4] = 0;
132
+ state = 3;
133
133
  }
134
-
135
- // Slide the window on by two runs: the trailing dark run of a rejected
136
- // candidate is often the leading dark run of the real one.
137
- counts[0] = counts[2];
138
- counts[1] = counts[3];
139
- counts[2] = counts[4];
140
- counts[3] = 1;
141
- counts[4] = 0;
142
- state = 3;
143
- }
144
-
145
- if (state === 4) {
146
- const moduleSize = matchRatios(counts, ratios);
147
- if (moduleSize > 0) {
148
- onHit(width - counts[4] - counts[3] - counts[2] / 2, moduleSize);
134
+ if (state === 4) {
135
+ const moduleSize = matchRatios(counts, ratios);
136
+ if (moduleSize > 0) {
137
+ onHit(width - counts[4] - counts[3] - counts[2] / 2, moduleSize);
138
+ }
149
139
  }
150
- }
151
140
  }
152
-
153
141
  /**
154
142
  * Walk a line through a candidate centre and confirm the ratio holds there too.
155
143
  *
@@ -161,51 +149,66 @@ function scanRow(image, y, ratios, onHit) {
161
149
  * @returns {number} Refined centre offset along the line, or NaN.
162
150
  */
163
151
  function crossCheck(image, x, y, dx, dy, ratios, maxRun) {
164
- const width = image.width;
165
- const height = image.height;
166
-
167
- /** @returns {boolean | null} Null when the sample falls outside the image. */
168
- const at = (i) => {
169
- const px = x + dx * i;
170
- const py = y + dy * i;
171
- if (px < 0 || py < 0 || px >= width || py >= height) return null;
172
- return image.get(px, py);
173
- };
174
-
175
- if (at(0) !== true) return NaN;
176
-
177
- const counts = [0, 0, 0, 0, 0];
178
- let i = 0;
179
-
180
- // Forward from the centre: rest of the centre run, then light, then dark.
181
- while (at(i) === true && counts[2] < maxRun) { counts[2]++; i++; }
182
- if (at(i) === null) return NaN;
183
- const centreForward = counts[2];
184
-
185
- while (at(i) === false && counts[3] < maxRun) { counts[3]++; i++; }
186
- if (at(i) === null || counts[3] === 0) return NaN;
187
-
188
- while (at(i) === true && counts[4] < maxRun) { counts[4]++; i++; }
189
- if (counts[4] === 0) return NaN;
190
-
191
- // Backward from the centre.
192
- i = -1;
193
- while (at(i) === true && counts[2] < maxRun * 2) { counts[2]++; i--; }
194
- if (at(i) === null) return NaN;
195
- const centreBackward = counts[2] - centreForward;
196
-
197
- while (at(i) === false && counts[1] < maxRun) { counts[1]++; i--; }
198
- if (at(i) === null || counts[1] === 0) return NaN;
199
-
200
- while (at(i) === true && counts[0] < maxRun) { counts[0]++; i--; }
201
- if (counts[0] === 0) return NaN;
202
-
203
- if (matchRatios(counts, ratios) === 0) return NaN;
204
-
205
- // The centre run spans offsets [-centreBackward, centreForward - 1].
206
- return (centreForward - 1 - centreBackward) / 2;
152
+ const width = image.width;
153
+ const height = image.height;
154
+ /** @returns {boolean | null} Null when the sample falls outside the image. */
155
+ const at = (i) => {
156
+ const px = x + dx * i;
157
+ const py = y + dy * i;
158
+ if (px < 0 || py < 0 || px >= width || py >= height)
159
+ return null;
160
+ return image.get(px, py);
161
+ };
162
+ if (at(0) !== true)
163
+ return NaN;
164
+ const counts = [0, 0, 0, 0, 0];
165
+ let i = 0;
166
+ // Forward from the centre: rest of the centre run, then light, then dark.
167
+ while (at(i) === true && counts[2] < maxRun) {
168
+ counts[2]++;
169
+ i++;
170
+ }
171
+ if (at(i) === null)
172
+ return NaN;
173
+ const centreForward = counts[2];
174
+ while (at(i) === false && counts[3] < maxRun) {
175
+ counts[3]++;
176
+ i++;
177
+ }
178
+ if (at(i) === null || counts[3] === 0)
179
+ return NaN;
180
+ while (at(i) === true && counts[4] < maxRun) {
181
+ counts[4]++;
182
+ i++;
183
+ }
184
+ if (counts[4] === 0)
185
+ return NaN;
186
+ // Backward from the centre.
187
+ i = -1;
188
+ while (at(i) === true && counts[2] < maxRun * 2) {
189
+ counts[2]++;
190
+ i--;
191
+ }
192
+ if (at(i) === null)
193
+ return NaN;
194
+ const centreBackward = counts[2] - centreForward;
195
+ while (at(i) === false && counts[1] < maxRun) {
196
+ counts[1]++;
197
+ i--;
198
+ }
199
+ if (at(i) === null || counts[1] === 0)
200
+ return NaN;
201
+ while (at(i) === true && counts[0] < maxRun) {
202
+ counts[0]++;
203
+ i--;
204
+ }
205
+ if (counts[0] === 0)
206
+ return NaN;
207
+ if (matchRatios(counts, ratios) === 0)
208
+ return NaN;
209
+ // The centre run spans offsets [-centreBackward, centreForward - 1].
210
+ return (centreForward - 1 - centreBackward) / 2;
207
211
  }
208
-
209
212
  /**
210
213
  * @typedef {object} Candidate
211
214
  * @property {number} x
@@ -213,7 +216,6 @@ function crossCheck(image, x, y, dx, dy, ratios, maxRun) {
213
216
  * @property {number} moduleSize
214
217
  * @property {number} hits
215
218
  */
216
-
217
219
  /**
218
220
  * Locate pattern centres of a given run ratio.
219
221
  *
@@ -224,61 +226,52 @@ function crossCheck(image, x, y, dx, dy, ratios, maxRun) {
224
226
  * @returns {Candidate[]}
225
227
  */
226
228
  function findPatterns(image, ratios, unitsWide, region) {
227
- /** @type {Candidate[]} */
228
- const found = [];
229
-
230
- const y0 = region ? Math.max(0, region.y0) : 0;
231
- const y1 = region ? Math.min(image.height, region.y1) : image.height;
232
-
233
- for (let y = y0; y < y1; y++) {
234
- scanRow(image, y, ratios, (centreX, moduleSize) => {
235
- if (region && (centreX < region.x0 || centreX > region.x1)) return;
236
-
237
- const px = Math.floor(centreX);
238
- const maxRun = Math.ceil(moduleSize * unitsWide);
239
- const offsetY = crossCheck(image, px, y, 0, 1, ratios, maxRun);
240
- if (Number.isNaN(offsetY)) return;
241
-
242
- const cy = y + offsetY;
243
- // Re-check horizontally at the refined row, which both confirms the hit
244
- // and gives a better x than the original scan line did.
245
- const offsetX = crossCheck(image, px, Math.round(cy), 1, 0, ratios, maxRun);
246
- if (Number.isNaN(offsetX)) return;
247
-
248
- const cx = px + offsetX;
249
-
250
- // Merge with an existing centre when they describe the same pattern.
251
- for (let i = 0; i < found.length; i++) {
252
- const c = found[i];
253
- if (
254
- Math.abs(c.x - cx) <= c.moduleSize &&
255
- Math.abs(c.y - cy) <= c.moduleSize &&
256
- Math.abs(c.moduleSize - moduleSize) <= Math.max(1, c.moduleSize / 2)
257
- ) {
258
- const n = c.hits + 1;
259
- c.x = (c.x * c.hits + cx) / n;
260
- c.y = (c.y * c.hits + cy) / n;
261
- c.moduleSize = (c.moduleSize * c.hits + moduleSize) / n;
262
- c.hits = n;
263
- return;
264
- }
265
- }
266
-
267
- found.push({ x: cx, y: cy, moduleSize, hits: 1 });
268
- });
269
- }
270
-
271
- return found;
229
+ /** @type {Candidate[]} */
230
+ const found = [];
231
+ const y0 = region ? Math.max(0, region.y0) : 0;
232
+ const y1 = region ? Math.min(image.height, region.y1) : image.height;
233
+ for (let y = y0; y < y1; y++) {
234
+ scanRow(image, y, ratios, (centreX, moduleSize) => {
235
+ if (region && (centreX < region.x0 || centreX > region.x1))
236
+ return;
237
+ const px = Math.floor(centreX);
238
+ const maxRun = Math.ceil(moduleSize * unitsWide);
239
+ const offsetY = crossCheck(image, px, y, 0, 1, ratios, maxRun);
240
+ if (Number.isNaN(offsetY))
241
+ return;
242
+ const cy = y + offsetY;
243
+ // Re-check horizontally at the refined row, which both confirms the hit
244
+ // and gives a better x than the original scan line did.
245
+ const offsetX = crossCheck(image, px, Math.round(cy), 1, 0, ratios, maxRun);
246
+ if (Number.isNaN(offsetX))
247
+ return;
248
+ const cx = px + offsetX;
249
+ // Merge with an existing centre when they describe the same pattern.
250
+ for (let i = 0; i < found.length; i++) {
251
+ const c = found[i];
252
+ if (Math.abs(c.x - cx) <= c.moduleSize &&
253
+ Math.abs(c.y - cy) <= c.moduleSize &&
254
+ Math.abs(c.moduleSize - moduleSize) <= Math.max(1, c.moduleSize / 2)) {
255
+ const n = c.hits + 1;
256
+ c.x = (c.x * c.hits + cx) / n;
257
+ c.y = (c.y * c.hits + cy) / n;
258
+ c.moduleSize = (c.moduleSize * c.hits + moduleSize) / n;
259
+ c.hits = n;
260
+ return;
261
+ }
262
+ }
263
+ found.push({ x: cx, y: cy, moduleSize, hits: 1 });
264
+ });
265
+ }
266
+ return found;
272
267
  }
273
-
274
268
  /**
275
269
  * @param {{x: number, y: number}} a @param {{x: number, y: number}} b
276
270
  * @returns {number}
277
271
  */
278
272
  function distance(a, b) {
279
- return Math.hypot(a.x - b.x, a.y - b.y);
273
+ return Math.hypot(a.x - b.x, a.y - b.y);
280
274
  }
281
-
282
275
  /**
283
276
  * Order three finder centres as top-left, top-right, bottom-left.
284
277
  *
@@ -291,33 +284,48 @@ function distance(a, b) {
291
284
  * @returns {{tl: Candidate, tr: Candidate, bl: Candidate} | null}
292
285
  */
293
286
  function orientFinders(three) {
294
- const [a, b, c] = three;
295
- const ab = distance(a, b);
296
- const bc = distance(b, c);
297
- const ca = distance(c, a);
298
-
299
- let tl, p, q, hypotenuse, leg1, leg2;
300
- if (ab >= bc && ab >= ca) {
301
- tl = c; p = a; q = b; hypotenuse = ab; leg1 = ca; leg2 = bc;
302
- } else if (bc >= ab && bc >= ca) {
303
- tl = a; p = b; q = c; hypotenuse = bc; leg1 = ab; leg2 = ca;
304
- } else {
305
- tl = b; p = c; q = a; hypotenuse = ca; leg1 = bc; leg2 = ab;
306
- }
307
-
308
- if (leg1 === 0 || leg2 === 0) return null;
309
-
310
- // The two legs must be near enough equal, and Pythagoras must hold: this is
311
- // what rejects three unrelated finder-lookalikes that happen to co-occur.
312
- const ratio = leg1 / leg2;
313
- if (ratio < 0.7 || ratio > 1.4) return null;
314
- const expected = Math.hypot(leg1, leg2);
315
- if (Math.abs(hypotenuse - expected) > expected * 0.25) return null;
316
-
317
- const cross = (p.x - tl.x) * (q.y - tl.y) - (p.y - tl.y) * (q.x - tl.x);
318
- return cross >= 0 ? { tl, tr: p, bl: q } : { tl, tr: q, bl: p };
287
+ const [a, b, c] = three;
288
+ const ab = distance(a, b);
289
+ const bc = distance(b, c);
290
+ const ca = distance(c, a);
291
+ let tl, p, q, hypotenuse, leg1, leg2;
292
+ if (ab >= bc && ab >= ca) {
293
+ tl = c;
294
+ p = a;
295
+ q = b;
296
+ hypotenuse = ab;
297
+ leg1 = ca;
298
+ leg2 = bc;
299
+ }
300
+ else if (bc >= ab && bc >= ca) {
301
+ tl = a;
302
+ p = b;
303
+ q = c;
304
+ hypotenuse = bc;
305
+ leg1 = ab;
306
+ leg2 = ca;
307
+ }
308
+ else {
309
+ tl = b;
310
+ p = c;
311
+ q = a;
312
+ hypotenuse = ca;
313
+ leg1 = bc;
314
+ leg2 = ab;
315
+ }
316
+ if (leg1 === 0 || leg2 === 0)
317
+ return null;
318
+ // The two legs must be near enough equal, and Pythagoras must hold: this is
319
+ // what rejects three unrelated finder-lookalikes that happen to co-occur.
320
+ const ratio = leg1 / leg2;
321
+ if (ratio < 0.7 || ratio > 1.4)
322
+ return null;
323
+ const expected = Math.hypot(leg1, leg2);
324
+ if (Math.abs(hypotenuse - expected) > expected * 0.25)
325
+ return null;
326
+ const cross = (p.x - tl.x) * (q.y - tl.y) - (p.y - tl.y) * (q.x - tl.x);
327
+ return cross >= 0 ? { tl, tr: p, bl: q } : { tl, tr: q, bl: p };
319
328
  }
320
-
321
329
  /**
322
330
  * Snap a measured dimension to a legal symbol size.
323
331
  *
@@ -329,26 +337,29 @@ function orientFinders(three) {
329
337
  * @returns {number} 0 if it cannot be reconciled.
330
338
  */
331
339
  function snapDimension(raw) {
332
- let d = Math.round(raw);
333
- switch (d & 3) {
334
- case 0: d--; break;
335
- case 2: d++; break;
336
- case 3: return 0;
337
- default: break;
338
- }
339
- if (d < MIN_DIMENSION || d > MAX_DIMENSION) return 0;
340
- return d;
340
+ let d = Math.round(raw);
341
+ switch (d & 3) {
342
+ case 0:
343
+ d--;
344
+ break;
345
+ case 2:
346
+ d++;
347
+ break;
348
+ case 3: return 0;
349
+ default: break;
350
+ }
351
+ if (d < MIN_DIMENSION || d > MAX_DIMENSION)
352
+ return 0;
353
+ return d;
341
354
  }
342
-
343
355
  /** The alignment pattern, as modules. 1 is dark. */
344
356
  const ALIGNMENT_MODULES = [
345
- [1, 1, 1, 1, 1],
346
- [1, 0, 0, 0, 1],
347
- [1, 0, 1, 0, 1],
348
- [1, 0, 0, 0, 1],
349
- [1, 1, 1, 1, 1],
357
+ [1, 1, 1, 1, 1],
358
+ [1, 0, 0, 0, 1],
359
+ [1, 0, 1, 0, 1],
360
+ [1, 0, 0, 0, 1],
361
+ [1, 1, 1, 1, 1],
350
362
  ];
351
-
352
363
  /**
353
364
  * Confirm a candidate by reading the 5x5 module block it claims to be.
354
365
  *
@@ -364,19 +375,20 @@ const ALIGNMENT_MODULES = [
364
375
  * @returns {boolean}
365
376
  */
366
377
  function verifyAlignment(image, cx, cy, moduleSize) {
367
- let good = 0;
368
- for (let j = 0; j < 5; j++) {
369
- for (let i = 0; i < 5; i++) {
370
- const px = Math.round(cx + (i - 2) * moduleSize);
371
- const py = Math.round(cy + (j - 2) * moduleSize);
372
- if (px < 0 || py < 0 || px >= image.width || py >= image.height) return false;
373
- if (image.get(px, py) === (ALIGNMENT_MODULES[j][i] === 1)) good++;
378
+ let good = 0;
379
+ for (let j = 0; j < 5; j++) {
380
+ for (let i = 0; i < 5; i++) {
381
+ const px = Math.round(cx + (i - 2) * moduleSize);
382
+ const py = Math.round(cy + (j - 2) * moduleSize);
383
+ if (px < 0 || py < 0 || px >= image.width || py >= image.height)
384
+ return false;
385
+ if (image.get(px, py) === (ALIGNMENT_MODULES[j][i] === 1))
386
+ good++;
387
+ }
374
388
  }
375
- }
376
- // Allow two modules of slop for blur and sampling, but no more.
377
- return good >= 23;
389
+ // Allow two modules of slop for blur and sampling, but no more.
390
+ return good >= 23;
378
391
  }
379
-
380
392
  /**
381
393
  * Look for the bottom-right alignment pattern near where the geometry predicts.
382
394
  *
@@ -386,42 +398,39 @@ function verifyAlignment(image, cx, cy, moduleSize) {
386
398
  * @returns {{x: number, y: number} | null}
387
399
  */
388
400
  function findAlignment(image, expected, moduleSize) {
389
- // Three modules of slack. The parallelogram estimate is good to well under
390
- // that on any symbol flat enough to decode, and a wider net only admits more
391
- // data modules as candidates.
392
- const radius = Math.max(3, Math.ceil(moduleSize * 3));
393
- const region = {
394
- x0: expected.x - radius,
395
- x1: expected.x + radius,
396
- y0: Math.floor(expected.y - radius),
397
- y1: Math.ceil(expected.y + radius),
398
- };
399
-
400
- const found = findPatterns(image, ALIGNMENT_RATIOS, 5, region);
401
-
402
- let best = null;
403
- let bestDistance = Infinity;
404
- for (let i = 0; i < found.length; i++) {
405
- // The alignment pattern is 5 modules across, so its implied module size
406
- // should agree with the one the finders reported.
407
- if (found[i].moduleSize > moduleSize * 1.5 || found[i].moduleSize < moduleSize / 1.5) continue;
408
- if (!verifyAlignment(image, found[i].x, found[i].y, moduleSize)) continue;
409
- const d = distance(found[i], expected);
410
- if (d < bestDistance) {
411
- bestDistance = d;
412
- best = found[i];
401
+ // Three modules of slack. The parallelogram estimate is good to well under
402
+ // that on any symbol flat enough to decode, and a wider net only admits more
403
+ // data modules as candidates.
404
+ const radius = Math.max(3, Math.ceil(moduleSize * 3));
405
+ const region = {
406
+ x0: expected.x - radius,
407
+ x1: expected.x + radius,
408
+ y0: Math.floor(expected.y - radius),
409
+ y1: Math.ceil(expected.y + radius),
410
+ };
411
+ const found = findPatterns(image, ALIGNMENT_RATIOS, 5, region);
412
+ let best = null;
413
+ let bestDistance = Infinity;
414
+ for (let i = 0; i < found.length; i++) {
415
+ // The alignment pattern is 5 modules across, so its implied module size
416
+ // should agree with the one the finders reported.
417
+ if (found[i].moduleSize > moduleSize * 1.5 || found[i].moduleSize < moduleSize / 1.5)
418
+ continue;
419
+ if (!verifyAlignment(image, found[i].x, found[i].y, moduleSize))
420
+ continue;
421
+ const d = distance(found[i], expected);
422
+ if (d < bestDistance) {
423
+ bestDistance = d;
424
+ best = found[i];
425
+ }
426
+ }
427
+ // Last resort: the pattern is exactly where predicted but its runs were
428
+ // mangled by blur. Reading the modules directly still confirms it.
429
+ if (!best && verifyAlignment(image, expected.x, expected.y, moduleSize)) {
430
+ best = { x: expected.x, y: expected.y };
413
431
  }
414
- }
415
-
416
- // Last resort: the pattern is exactly where predicted but its runs were
417
- // mangled by blur. Reading the modules directly still confirms it.
418
- if (!best && verifyAlignment(image, expected.x, expected.y, moduleSize)) {
419
- best = { x: expected.x, y: expected.y };
420
- }
421
-
422
- return best;
432
+ return best;
423
433
  }
424
-
425
434
  /**
426
435
  * @typedef {object} Detection
427
436
  * @property {Array<{x: number, y: number}>} corners Outer corners of the
@@ -431,7 +440,6 @@ function findAlignment(image, expected, moduleSize) {
431
440
  * @property {number} moduleSize Estimated pixels per module.
432
441
  * @property {boolean} alignmentFound
433
442
  */
434
-
435
443
  /**
436
444
  * Find QR Code symbols in a binarized image.
437
445
  *
@@ -440,63 +448,55 @@ function findAlignment(image, expected, moduleSize) {
440
448
  * the most prominent symbol comes first.
441
449
  */
442
450
  export function detectQR(binaryImage) {
443
- if (!binaryImage || !binaryImage.width) {
444
- throw new NotFoundError('detectQR: no image supplied');
445
- }
446
-
447
- const finders = findPatterns(binaryImage, FINDER_RATIOS, 7);
448
- // A single stray row hit is noise; a real finder is crossed many times.
449
- const solid = finders.filter((f) => f.hits >= 2);
450
- const pool = solid.length >= 3 ? solid : finders;
451
- if (pool.length < 3) return [];
452
-
453
- // Prefer the largest patterns, and cap the combinatorics on noisy images.
454
- pool.sort((a, b) => b.moduleSize - a.moduleSize || b.hits - a.hits);
455
- const limit = Math.min(pool.length, 12);
456
-
457
- /** @type {Detection[]} */
458
- const detections = [];
459
- const used = new Set();
460
-
461
- for (let i = 0; i < limit; i++) {
462
- for (let j = i + 1; j < limit; j++) {
463
- for (let k = j + 1; k < limit; k++) {
464
- const three = [pool[i], pool[j], pool[k]];
465
-
466
- // All three finders belong to one symbol, so they share a module size.
467
- const sizes = three.map((f) => f.moduleSize);
468
- if (Math.max(...sizes) > Math.min(...sizes) * 1.6) continue;
469
-
470
- const oriented = orientFinders(three);
471
- if (!oriented) continue;
472
-
473
- const { tl, tr, bl } = oriented;
474
- const moduleSize = (tl.moduleSize + tr.moduleSize + bl.moduleSize) / 3;
475
- if (moduleSize <= 0) continue;
476
-
477
- // Centre-to-centre spans dimension - 7 modules.
478
- const across = (distance(tl, tr) + distance(tl, bl)) / 2;
479
- const dimension = snapDimension(across / moduleSize + 7);
480
- if (dimension === 0) continue;
481
-
482
- const version = (dimension - 17) / 4;
483
- if (version < 1 || version > 40) continue;
484
-
485
- const key = `${Math.round(tl.x)},${Math.round(tl.y)},${dimension}`;
486
- if (used.has(key)) continue;
487
- used.add(key);
488
-
489
- detections.push(
490
- buildDetection(binaryImage, tl, tr, bl, dimension, version, moduleSize)
491
- );
492
- }
451
+ if (!binaryImage || !binaryImage.width) {
452
+ throw new NotFoundError('detectQR: no image supplied');
453
+ }
454
+ const finders = findPatterns(binaryImage, FINDER_RATIOS, 7);
455
+ // A single stray row hit is noise; a real finder is crossed many times.
456
+ const solid = finders.filter((f) => f.hits >= 2);
457
+ const pool = solid.length >= 3 ? solid : finders;
458
+ if (pool.length < 3)
459
+ return [];
460
+ // Prefer the largest patterns, and cap the combinatorics on noisy images.
461
+ pool.sort((a, b) => b.moduleSize - a.moduleSize || b.hits - a.hits);
462
+ const limit = Math.min(pool.length, 12);
463
+ /** @type {Detection[]} */
464
+ const detections = [];
465
+ const used = new Set();
466
+ for (let i = 0; i < limit; i++) {
467
+ for (let j = i + 1; j < limit; j++) {
468
+ for (let k = j + 1; k < limit; k++) {
469
+ const three = [pool[i], pool[j], pool[k]];
470
+ // All three finders belong to one symbol, so they share a module size.
471
+ const sizes = three.map((f) => f.moduleSize);
472
+ if (Math.max(...sizes) > Math.min(...sizes) * 1.6)
473
+ continue;
474
+ const oriented = orientFinders(three);
475
+ if (!oriented)
476
+ continue;
477
+ const { tl, tr, bl } = oriented;
478
+ const moduleSize = (tl.moduleSize + tr.moduleSize + bl.moduleSize) / 3;
479
+ if (moduleSize <= 0)
480
+ continue;
481
+ // Centre-to-centre spans dimension - 7 modules.
482
+ const across = (distance(tl, tr) + distance(tl, bl)) / 2;
483
+ const dimension = snapDimension(across / moduleSize + 7);
484
+ if (dimension === 0)
485
+ continue;
486
+ const version = (dimension - 17) / 4;
487
+ if (version < 1 || version > 40)
488
+ continue;
489
+ const key = `${Math.round(tl.x)},${Math.round(tl.y)},${dimension}`;
490
+ if (used.has(key))
491
+ continue;
492
+ used.add(key);
493
+ detections.push(buildDetection(binaryImage, tl, tr, bl, dimension, version, moduleSize));
494
+ }
495
+ }
493
496
  }
494
- }
495
-
496
- detections.sort((a, b) => b.moduleSize - a.moduleSize);
497
- return detections;
497
+ detections.sort((a, b) => b.moduleSize - a.moduleSize);
498
+ return detections;
498
499
  }
499
-
500
500
  /**
501
501
  * Turn three finder centres into four symbol corners.
502
502
  *
@@ -506,56 +506,42 @@ export function detectQR(binaryImage) {
506
506
  * @returns {Detection}
507
507
  */
508
508
  function buildDetection(image, tl, tr, bl, dimension, version, moduleSize) {
509
- const d = dimension;
510
- // Finder centres sit on module (3, 3) and friends, so at grid coordinate 3.5.
511
- const gridTl = [3.5, 3.5];
512
- const gridTr = [d - 3.5, 3.5];
513
- const gridBl = [3.5, d - 3.5];
514
-
515
- // Parallelogram estimate of the far corner, used both as the search seed for
516
- // the alignment pattern and as the fallback when it is not there.
517
- const guess = { x: tr.x + bl.x - tl.x, y: tr.y + bl.y - tl.y };
518
-
519
- /** @type {PerspectiveTransform | null} */
520
- let transform = null;
521
- let alignmentFound = false;
522
-
523
- if (version >= 2) {
524
- // The bottom-right alignment pattern is centred on module (d - 7, d - 7).
525
- const gridAlign = [d - 6.5, d - 6.5];
526
- // Where that module lands under the parallelogram assumption.
527
- const seed = {
528
- x: tl.x + ((gridAlign[0] - 3.5) / (d - 7)) * (tr.x - tl.x) +
529
- ((gridAlign[1] - 3.5) / (d - 7)) * (bl.x - tl.x),
530
- y: tl.y + ((gridAlign[0] - 3.5) / (d - 7)) * (tr.y - tl.y) +
531
- ((gridAlign[1] - 3.5) / (d - 7)) * (bl.y - tl.y),
532
- };
533
-
534
- const align = findAlignment(image, seed, moduleSize);
535
- if (align) {
536
- alignmentFound = true;
537
- transform = PerspectiveTransform.quadToQuad(
538
- gridTl[0], gridTl[1], gridTr[0], gridTr[1], gridAlign[0], gridAlign[1], gridBl[0], gridBl[1],
539
- tl.x, tl.y, tr.x, tr.y, align.x, align.y, bl.x, bl.y
540
- );
509
+ const d = dimension;
510
+ // Finder centres sit on module (3, 3) and friends, so at grid coordinate 3.5.
511
+ const gridTl = [3.5, 3.5];
512
+ const gridTr = [d - 3.5, 3.5];
513
+ const gridBl = [3.5, d - 3.5];
514
+ // Parallelogram estimate of the far corner, used both as the search seed for
515
+ // the alignment pattern and as the fallback when it is not there.
516
+ const guess = { x: tr.x + bl.x - tl.x, y: tr.y + bl.y - tl.y };
517
+ /** @type {PerspectiveTransform | null} */
518
+ let transform = null;
519
+ let alignmentFound = false;
520
+ if (version >= 2) {
521
+ // The bottom-right alignment pattern is centred on module (d - 7, d - 7).
522
+ const gridAlign = [d - 6.5, d - 6.5];
523
+ // Where that module lands under the parallelogram assumption.
524
+ const seed = {
525
+ x: tl.x + ((gridAlign[0] - 3.5) / (d - 7)) * (tr.x - tl.x) +
526
+ ((gridAlign[1] - 3.5) / (d - 7)) * (bl.x - tl.x),
527
+ y: tl.y + ((gridAlign[0] - 3.5) / (d - 7)) * (tr.y - tl.y) +
528
+ ((gridAlign[1] - 3.5) / (d - 7)) * (bl.y - tl.y),
529
+ };
530
+ const align = findAlignment(image, seed, moduleSize);
531
+ if (align) {
532
+ alignmentFound = true;
533
+ transform = PerspectiveTransform.quadToQuad(gridTl[0], gridTl[1], gridTr[0], gridTr[1], gridAlign[0], gridAlign[1], gridBl[0], gridBl[1], tl.x, tl.y, tr.x, tr.y, align.x, align.y, bl.x, bl.y);
534
+ }
541
535
  }
542
- }
543
-
544
- const plain = PerspectiveTransform.quadToQuad(
545
- gridTl[0], gridTl[1], gridTr[0], gridTr[1], d - 3.5, d - 3.5, gridBl[0], gridBl[1],
546
- tl.x, tl.y, tr.x, tr.y, guess.x, guess.y, bl.x, bl.y
547
- );
548
-
549
- const corners = cornersOf(transform ?? plain, d);
550
- // Keep the parallelogram corners as a second opinion whenever an alignment
551
- // pattern steered the first set. Verification makes a false match unlikely,
552
- // not impossible, and one extra sampling attempt is far cheaper than losing
553
- // a symbol to it.
554
- const altCorners = transform ? cornersOf(plain, d) : null;
555
-
556
- return { corners, altCorners, dimension, version, moduleSize, alignmentFound };
536
+ const plain = PerspectiveTransform.quadToQuad(gridTl[0], gridTl[1], gridTr[0], gridTr[1], d - 3.5, d - 3.5, gridBl[0], gridBl[1], tl.x, tl.y, tr.x, tr.y, guess.x, guess.y, bl.x, bl.y);
537
+ const corners = cornersOf(transform ?? plain, d);
538
+ // Keep the parallelogram corners as a second opinion whenever an alignment
539
+ // pattern steered the first set. Verification makes a false match unlikely,
540
+ // not impossible, and one extra sampling attempt is far cheaper than losing
541
+ // a symbol to it.
542
+ const altCorners = transform ? cornersOf(plain, d) : null;
543
+ return { corners, altCorners, dimension, version, moduleSize, alignmentFound };
557
544
  }
558
-
559
545
  /**
560
546
  * The four outer corners of the symbol under a grid-to-image transform.
561
547
  *
@@ -563,14 +549,13 @@ function buildDetection(image, tl, tr, bl, dimension, version, moduleSize) {
563
549
  * @returns {Array<{x: number, y: number}>}
564
550
  */
565
551
  function cornersOf(transform, d) {
566
- return [
567
- transform.transformPoint(0, 0),
568
- transform.transformPoint(d, 0),
569
- transform.transformPoint(d, d),
570
- transform.transformPoint(0, d),
571
- ];
552
+ return [
553
+ transform.transformPoint(0, 0),
554
+ transform.transformPoint(d, 0),
555
+ transform.transformPoint(d, d),
556
+ transform.transformPoint(0, d),
557
+ ];
572
558
  }
573
-
574
559
  /**
575
560
  * Find and decode every QR Code in a binarized image.
576
561
  *
@@ -586,45 +571,43 @@ function cornersOf(transform, d) {
586
571
  * Empty when nothing decodes; never throws for "no symbol here".
587
572
  */
588
573
  export function detectAndDecodeQR(binaryImage) {
589
- let detections;
590
- try {
591
- detections = detectQR(binaryImage);
592
- } catch (e) {
593
- return [];
594
- }
595
-
596
- const results = [];
597
- const seen = new Set();
598
-
599
- for (let i = 0; i < detections.length; i++) {
600
- const det = detections[i];
601
-
602
- for (let attempt = 0; attempt < 4; attempt++) {
603
- const voting = attempt === 1 || attempt === 3;
604
- const rotated = attempt >= 2;
605
-
606
- let matrix;
607
- try {
608
- matrix = sampleQuad(binaryImage, det.dimension, det.corners, voting);
609
- } catch (e) {
610
- continue;
611
- }
612
- if (rotated) matrix.rotate180();
613
-
614
- try {
615
- const result = decodeQR(matrix);
616
- // The same symbol can be detected through more than one finder triple.
617
- const key = `${result.version}|${result.text}`;
618
- if (!seen.has(key)) {
619
- seen.add(key);
620
- results.push(Object.assign({ corners: det.corners }, result));
574
+ let detections;
575
+ try {
576
+ detections = detectQR(binaryImage);
577
+ }
578
+ catch (e) {
579
+ return [];
580
+ }
581
+ const results = [];
582
+ const seen = new Set();
583
+ for (let i = 0; i < detections.length; i++) {
584
+ const det = detections[i];
585
+ for (let attempt = 0; attempt < 4; attempt++) {
586
+ const voting = attempt === 1 || attempt === 3;
587
+ const rotated = attempt >= 2;
588
+ let matrix;
589
+ try {
590
+ matrix = sampleQuad(binaryImage, det.dimension, det.corners, voting);
591
+ }
592
+ catch (e) {
593
+ continue;
594
+ }
595
+ if (rotated)
596
+ matrix.rotate180();
597
+ try {
598
+ const result = decodeQR(matrix);
599
+ // The same symbol can be detected through more than one finder triple.
600
+ const key = `${result.version}|${result.text}`;
601
+ if (!seen.has(key)) {
602
+ seen.add(key);
603
+ results.push(Object.assign({ corners: det.corners }, result));
604
+ }
605
+ break;
606
+ }
607
+ catch (e) {
608
+ /* Try the next sampling strategy. */
609
+ }
621
610
  }
622
- break;
623
- } catch (e) {
624
- /* Try the next sampling strategy. */
625
- }
626
611
  }
627
- }
628
-
629
- return results;
612
+ return results;
630
613
  }