qr 0.5.2 → 0.5.4
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 +28 -26
- package/decode.d.ts +2 -1
- package/decode.d.ts.map +1 -1
- package/decode.js +73 -23
- package/decode.js.map +1 -1
- package/dom.d.ts +1 -0
- package/dom.d.ts.map +1 -1
- package/dom.js +4 -1
- package/dom.js.map +1 -1
- package/index.d.ts +29 -5
- package/index.d.ts.map +1 -1
- package/index.js +456 -139
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/src/decode.ts +72 -21
- package/src/dom.ts +5 -1
- package/src/index.ts +450 -135
package/src/index.ts
CHANGED
|
@@ -62,29 +62,36 @@ function fillArr<T>(length: number, val: T): T[] {
|
|
|
62
62
|
return new Array(length).fill(val);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function popcnt(n: number): number {
|
|
66
|
+
n = n - ((n >>> 1) & 0x55555555);
|
|
67
|
+
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
|
68
|
+
return (((n + (n >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* Interleaves byte blocks.
|
|
67
73
|
* @param blocks [[1, 2, 3], [4, 5, 6]]
|
|
68
74
|
* @returns [1, 4, 2, 5, 3, 6]
|
|
69
75
|
*/
|
|
70
|
-
function interleaveBytes(
|
|
71
|
-
let
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
function interleaveBytes(blocks: Uint8Array[]): Uint8Array {
|
|
77
|
+
let maxLen = 0;
|
|
78
|
+
let totalLen = 0;
|
|
79
|
+
for (const block of blocks) {
|
|
80
|
+
maxLen = Math.max(maxLen, block.length);
|
|
81
|
+
totalLen += block.length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result = new Uint8Array(totalLen);
|
|
85
|
+
let idx = 0;
|
|
86
|
+
for (let i = 0; i < maxLen; i++) {
|
|
87
|
+
for (const block of blocks) {
|
|
88
|
+
if (i < block.length) result[idx++] = block[i];
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
|
-
return new Uint8Array(res);
|
|
81
|
-
}
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
if (index < 0 || index + pattern.length > lst.length) return false;
|
|
85
|
-
for (let i = 0; i < pattern.length; i++) if (pattern[i] !== lst[index + i]) return false;
|
|
86
|
-
return true;
|
|
92
|
+
return result;
|
|
87
93
|
}
|
|
94
|
+
|
|
88
95
|
// Optimize for minimal score/penalty
|
|
89
96
|
function best<T>(): {
|
|
90
97
|
add(score: number, value: T): void;
|
|
@@ -134,6 +141,36 @@ function alphabet(
|
|
|
134
141
|
};
|
|
135
142
|
}
|
|
136
143
|
|
|
144
|
+
// Transpose 32x32 bit matrix in-place
|
|
145
|
+
// a[0..31] are 32 rows of 32 bits each; after transpose they become 32 columns.
|
|
146
|
+
function transpose32(a: Uint32Array) {
|
|
147
|
+
if (a.length !== 32) throw new Error('expects 32 element matrix');
|
|
148
|
+
const masks = [0x55555555, 0x33333333, 0x0f0f0f0f, 0x00ff00ff, 0x0000ffff] as const;
|
|
149
|
+
// Hello again, FFT
|
|
150
|
+
for (let stage = 0; stage < 5; stage++) {
|
|
151
|
+
const m = masks[stage] >>> 0;
|
|
152
|
+
const s = 1 << stage; // 1,2,4,8,16
|
|
153
|
+
const step = s << 1; // 2,4,8,16,32
|
|
154
|
+
for (let i = 0; i < 32; i += step) {
|
|
155
|
+
for (let k = 0; k < s; k++) {
|
|
156
|
+
const i0 = i + k;
|
|
157
|
+
const i1 = i0 + s;
|
|
158
|
+
const x = a[i0] >>> 0;
|
|
159
|
+
const y = a[i1] >>> 0;
|
|
160
|
+
const t = ((x >>> s) ^ y) & m;
|
|
161
|
+
a[i0] = (x ^ (t << s)) >>> 0;
|
|
162
|
+
a[i1] = (y ^ t) >>> 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const bitMask = (x: number): number => (1 << (x & 31)) >>> 0;
|
|
168
|
+
const rangeMask = (shift: number, len: number): number => {
|
|
169
|
+
// len in [0..32], shift in [0..31]
|
|
170
|
+
if (len === 0) return 0;
|
|
171
|
+
if (len === 32) return 0xffffffff;
|
|
172
|
+
return (((1 << len) - 1) << shift) >>> 0;
|
|
173
|
+
};
|
|
137
174
|
/*
|
|
138
175
|
Basic bitmap structure for two colors (black & white) small images.
|
|
139
176
|
- undefined is used as a marker whether cell was written or not
|
|
@@ -177,8 +214,8 @@ export class Bitmap {
|
|
|
177
214
|
s = s.replace(/^\n+/g, '').replace(/\n+$/g, '');
|
|
178
215
|
const lines = s.split(String.fromCharCode(chCodes.newline));
|
|
179
216
|
const height = lines.length;
|
|
180
|
-
const data = new Array(height);
|
|
181
217
|
let width: number | undefined;
|
|
218
|
+
const rows: DrawValue[][] = [];
|
|
182
219
|
for (const line of lines) {
|
|
183
220
|
const row = line.split('').map((i) => {
|
|
184
221
|
if (i === 'X') return true;
|
|
@@ -186,34 +223,53 @@ export class Bitmap {
|
|
|
186
223
|
if (i === '?') return undefined;
|
|
187
224
|
throw new Error(`Bitmap.fromString: unknown symbol=${i}`);
|
|
188
225
|
});
|
|
189
|
-
if (width && row.length !== width)
|
|
226
|
+
if (width !== undefined && row.length !== width)
|
|
190
227
|
throw new Error(`Bitmap.fromString different row sizes: width=${width} cur=${row.length}`);
|
|
191
228
|
width = row.length;
|
|
192
|
-
|
|
229
|
+
rows.push(row);
|
|
193
230
|
}
|
|
194
|
-
if (
|
|
195
|
-
return new Bitmap({ height, width },
|
|
231
|
+
if (width === undefined) width = 0;
|
|
232
|
+
return new Bitmap({ height, width }, rows);
|
|
196
233
|
}
|
|
197
|
-
|
|
198
|
-
|
|
234
|
+
// Two bitsets:
|
|
235
|
+
// defined=0 -> undefined
|
|
236
|
+
// defined=1,value=0 -> false
|
|
237
|
+
// defined=1,value=1 -> true
|
|
238
|
+
private defined: Uint32Array;
|
|
239
|
+
private value: Uint32Array;
|
|
240
|
+
private tailMask: number;
|
|
241
|
+
private words: number;
|
|
242
|
+
private fullWords: number;
|
|
199
243
|
height: number;
|
|
200
244
|
width: number;
|
|
201
245
|
constructor(size: Size | number, data?: DrawValue[][]) {
|
|
202
246
|
const { height, width } = Bitmap.size(size);
|
|
203
|
-
this.data = data || Array.from({ length: height }, () => fillArr(width, undefined));
|
|
204
247
|
this.height = height;
|
|
205
248
|
this.width = width;
|
|
249
|
+
this.tailMask = rangeMask(0, width & 31 || 32);
|
|
250
|
+
this.words = Math.ceil(width / 32) | 0;
|
|
251
|
+
this.fullWords = Math.floor(width / 32) | 0;
|
|
252
|
+
this.value = new Uint32Array(this.words * height);
|
|
253
|
+
this.defined = new Uint32Array(this.value.length);
|
|
254
|
+
if (data) {
|
|
255
|
+
// accept same semantics as old version
|
|
256
|
+
if (data.length !== height)
|
|
257
|
+
throw new Error(`Bitmap: data height mismatch: exp=${height} got=${data.length}`);
|
|
258
|
+
for (let y = 0; y < height; y++) {
|
|
259
|
+
const row = data[y];
|
|
260
|
+
if (!row || row.length !== width)
|
|
261
|
+
throw new Error(`Bitmap: data width mismatch at y=${y}: exp=${width} got=${row?.length}`);
|
|
262
|
+
for (let x = 0; x < width; x++) this.set(x, y, row[x]);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
206
265
|
}
|
|
207
266
|
point(p: Point): DrawValue {
|
|
208
|
-
return this.
|
|
267
|
+
return this.get(p.x, p.y);
|
|
209
268
|
}
|
|
210
269
|
isInside(p: Point): boolean {
|
|
211
270
|
return 0 <= p.x && p.x < this.width && 0 <= p.y && p.y < this.height;
|
|
212
271
|
}
|
|
213
|
-
size(offset?: Point | number): {
|
|
214
|
-
height: number;
|
|
215
|
-
width: number;
|
|
216
|
-
} {
|
|
272
|
+
size(offset?: Point | number): { height: number; width: number } {
|
|
217
273
|
if (!offset) return { height: this.height, width: this.width };
|
|
218
274
|
const { x, y } = this.xy(offset);
|
|
219
275
|
return { height: this.height - y, width: this.width - x };
|
|
@@ -227,27 +283,113 @@ export class Bitmap {
|
|
|
227
283
|
c.y = mod(c.y, this.height);
|
|
228
284
|
return c;
|
|
229
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Return pixel bit index
|
|
288
|
+
*/
|
|
289
|
+
private wordIndex(x: number, y: number): number {
|
|
290
|
+
return y * this.words + (x >>> 5);
|
|
291
|
+
}
|
|
292
|
+
private bitIndex(x: number, y: number) {
|
|
293
|
+
return { word: this.wordIndex(x, y), bit: x & 31 };
|
|
294
|
+
}
|
|
295
|
+
isDefined(x: number, y: number): boolean {
|
|
296
|
+
const wi = this.wordIndex(x, y);
|
|
297
|
+
const m = bitMask(x);
|
|
298
|
+
return (this.defined[wi] & m) !== 0;
|
|
299
|
+
}
|
|
300
|
+
get(x: number, y: number): boolean {
|
|
301
|
+
const wi = this.wordIndex(x, y);
|
|
302
|
+
const m = bitMask(x);
|
|
303
|
+
return (this.value[wi] & m) !== 0;
|
|
304
|
+
}
|
|
305
|
+
private maskWord(wi: number, mask: number, v: boolean): void {
|
|
306
|
+
const { defined, value } = this;
|
|
307
|
+
defined[wi] |= mask;
|
|
308
|
+
value[wi] = (value[wi] & ~mask) | (-v & mask);
|
|
309
|
+
}
|
|
310
|
+
set(x: number, y: number, v: DrawValue): void {
|
|
311
|
+
if (v === undefined) return;
|
|
312
|
+
this.maskWord(this.wordIndex(x, y), bitMask(x), v);
|
|
313
|
+
}
|
|
314
|
+
// word-span fill for constant values (fast path)
|
|
315
|
+
private fillRectConst(x0: number, y0: number, w: number, h: number, v: DrawValue) {
|
|
316
|
+
if (w <= 0 || h <= 0) return;
|
|
317
|
+
if (v === undefined) return;
|
|
318
|
+
const { value, defined, words } = this;
|
|
319
|
+
const startWord = x0 >>> 5;
|
|
320
|
+
const endWord = (x0 + w - 1) >>> 5;
|
|
321
|
+
const startBit = x0 & 31;
|
|
322
|
+
const endBit = (x0 + w - 1) & 31;
|
|
323
|
+
for (let ry = 0; ry < h; ry++) {
|
|
324
|
+
const rowBase = (y0 + ry) * words;
|
|
325
|
+
if (startWord === endWord) {
|
|
326
|
+
const mask = rangeMask(startBit, endBit - startBit + 1);
|
|
327
|
+
this.maskWord(rowBase + startWord, mask, v);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
this.maskWord(rowBase + startWord, rangeMask(startBit, 32 - startBit), v);
|
|
331
|
+
for (let i = startWord + 1; i < endWord; i++) {
|
|
332
|
+
defined[rowBase + i] = 0xffffffff;
|
|
333
|
+
value[rowBase + i] = v ? 0xffffffff : 0;
|
|
334
|
+
}
|
|
335
|
+
this.maskWord(rowBase + endWord, rangeMask(0, endBit + 1), v);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
private rectWords(
|
|
339
|
+
x: number,
|
|
340
|
+
y: number,
|
|
341
|
+
width: number,
|
|
342
|
+
height: number,
|
|
343
|
+
cb: (wi: number, bitX: number, xPos: number, yPos: number, bitsInWord: number) => void
|
|
344
|
+
): void {
|
|
345
|
+
for (let yPos = 0; yPos < height; yPos++) {
|
|
346
|
+
const Py = y + yPos;
|
|
347
|
+
for (let xPos = 0; xPos < width; ) {
|
|
348
|
+
const bitX = x + xPos;
|
|
349
|
+
const { bit, word } = this.bitIndex(bitX, Py);
|
|
350
|
+
const bitsPerWord = Math.min(32 - bit, width - xPos);
|
|
351
|
+
cb(word, bitX, xPos, yPos, bitsPerWord);
|
|
352
|
+
xPos += bitsPerWord;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
230
356
|
// Basically every operation can be represented as rect
|
|
231
|
-
rect(c: Point | number, size: Size | number,
|
|
357
|
+
rect(c: Point | number, size: Size | number, fn: DrawFn): this {
|
|
232
358
|
const { x, y } = this.xy(c);
|
|
233
359
|
const { height, width } = Bitmap.size(size, this.size({ x, y }));
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this.data[y + yPos][x + xPos] =
|
|
238
|
-
typeof value === 'function'
|
|
239
|
-
? value({ x: xPos, y: yPos }, this.data[y + yPos][x + xPos])
|
|
240
|
-
: value;
|
|
241
|
-
}
|
|
360
|
+
if (typeof fn !== 'function') {
|
|
361
|
+
this.fillRectConst(x, y, width, height, fn);
|
|
362
|
+
return this;
|
|
242
363
|
}
|
|
364
|
+
const { defined, value } = this;
|
|
365
|
+
this.rectWords(x, y, width, height, (wi, bitX, xPos, yPos, n) => {
|
|
366
|
+
let defWord = 0;
|
|
367
|
+
let valWord = value[wi];
|
|
368
|
+
for (let b = 0; b < n; b++) {
|
|
369
|
+
const mask = bitMask(bitX + b);
|
|
370
|
+
const res = fn({ x: xPos + b, y: yPos }, (valWord & mask) !== 0);
|
|
371
|
+
if (res === undefined) continue;
|
|
372
|
+
defWord |= mask;
|
|
373
|
+
valWord = (valWord & ~mask) | (-res & mask);
|
|
374
|
+
}
|
|
375
|
+
defined[wi] |= defWord;
|
|
376
|
+
value[wi] = valWord;
|
|
377
|
+
});
|
|
243
378
|
return this;
|
|
244
379
|
}
|
|
245
380
|
// returns rectangular part of bitmap
|
|
246
381
|
rectRead(c: Point | number, size: Size | number, fn: ReadFn): this {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
382
|
+
const { x, y } = this.xy(c);
|
|
383
|
+
const { height, width } = Bitmap.size(size, this.size({ x, y }));
|
|
384
|
+
const { value } = this;
|
|
385
|
+
this.rectWords(x, y, width, height, (wi, bitX, xPos, yPos, n) => {
|
|
386
|
+
const valWord = value[wi];
|
|
387
|
+
for (let b = 0; b < n; b++) {
|
|
388
|
+
const mask = bitMask(bitX + b);
|
|
389
|
+
fn({ x: xPos + b, y: yPos }, (valWord & mask) !== 0);
|
|
390
|
+
}
|
|
250
391
|
});
|
|
392
|
+
return this;
|
|
251
393
|
}
|
|
252
394
|
// Horizontal & vertical lines
|
|
253
395
|
hLine(c: Point | number, len: number, value: DrawFn): this {
|
|
@@ -260,25 +402,91 @@ export class Bitmap {
|
|
|
260
402
|
border(border = 2, value: DrawValue): Bitmap {
|
|
261
403
|
const height = this.height + 2 * border;
|
|
262
404
|
const width = this.width + 2 * border;
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
405
|
+
const out = new Bitmap({ height, width });
|
|
406
|
+
// fill everything with border value, then embed original
|
|
407
|
+
out.rect(0, Infinity, value);
|
|
408
|
+
out.embed({ x: border, y: border }, this);
|
|
409
|
+
return out;
|
|
266
410
|
}
|
|
267
411
|
// Embed another bitmap on coordinates
|
|
268
|
-
embed(c: Point | number,
|
|
269
|
-
|
|
412
|
+
embed(c: Point | number, src: Bitmap): this {
|
|
413
|
+
const { x, y } = this.xy(c);
|
|
414
|
+
const { height, width } = Bitmap.size(src.size(), this.size({ x, y }));
|
|
415
|
+
if (width <= 0 || height <= 0) return this;
|
|
416
|
+
const { value, defined } = this;
|
|
417
|
+
const { words: srcStride, value: srcValue } = src;
|
|
418
|
+
for (let yPos = 0; yPos < height; yPos++) {
|
|
419
|
+
const srcRow = yPos * srcStride;
|
|
420
|
+
for (let xPos = 0; xPos < width; ) {
|
|
421
|
+
const dstX = x + xPos;
|
|
422
|
+
const { word: dstWord, bit: dstBit } = this.bitIndex(dstX, y + yPos);
|
|
423
|
+
const { word: srcWord, bit: srcBit } = src.bitIndex(xPos, yPos);
|
|
424
|
+
const len = Math.min(32 - dstBit, width - xPos);
|
|
425
|
+
const w0 = srcValue[srcWord];
|
|
426
|
+
const w1 = srcBit && srcWord + 1 < srcRow + srcStride ? srcValue[srcWord + 1] : 0;
|
|
427
|
+
const sVal = srcBit ? ((w0 >>> srcBit) | (w1 << (32 - srcBit))) >>> 0 : w0;
|
|
428
|
+
const dstMask = rangeMask(dstBit, len);
|
|
429
|
+
const valBits = ((sVal & rangeMask(0, len)) << dstBit) >>> 0;
|
|
430
|
+
defined[dstWord] |= dstMask;
|
|
431
|
+
value[dstWord] = (value[dstWord] & ~dstMask) | valBits;
|
|
432
|
+
xPos += len;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return this;
|
|
270
436
|
}
|
|
271
437
|
// returns rectangular part of bitmap
|
|
272
438
|
rectSlice(c: Point | number, size: Size | number = this.size()): Bitmap {
|
|
273
|
-
const
|
|
274
|
-
|
|
439
|
+
const { x, y } = this.xy(c);
|
|
440
|
+
const { height, width } = Bitmap.size(size, this.size({ x, y }));
|
|
441
|
+
const rect = new Bitmap({ height, width });
|
|
442
|
+
this.rectRead({ x, y }, { height, width }, (p, cur) => {
|
|
443
|
+
if (this.isDefined(x + p.x, y + p.y)) {
|
|
444
|
+
rect.set(p.x, p.y, cur);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
275
447
|
return rect;
|
|
276
448
|
}
|
|
277
449
|
// Change shape, replace rows with columns (data[y][x] -> data[x][y])
|
|
278
|
-
|
|
279
|
-
const { height, width } = this;
|
|
280
|
-
const
|
|
281
|
-
|
|
450
|
+
transpose(): Bitmap {
|
|
451
|
+
const { height, width, value, defined, words } = this;
|
|
452
|
+
const dst = new Bitmap({ height: width, width: height });
|
|
453
|
+
const { words: dstStride, value: dstValue, defined: dstDefined, tailMask: dstTail } = dst;
|
|
454
|
+
const tmpV = new Uint32Array(32);
|
|
455
|
+
const tmpD = new Uint32Array(32);
|
|
456
|
+
// Process src in blocks: y in [by..by+31], x in 32-bit words
|
|
457
|
+
for (let by = 0; by < height; by += 32) {
|
|
458
|
+
for (let bx = 0; bx < words; bx++) {
|
|
459
|
+
const rows = Math.min(32, height - by);
|
|
460
|
+
for (let r = 0; r < rows; r++) {
|
|
461
|
+
const wi = this.wordIndex(32 * bx, by + r);
|
|
462
|
+
tmpV[r] = value[wi];
|
|
463
|
+
tmpD[r] = defined[wi];
|
|
464
|
+
}
|
|
465
|
+
// zero-pad remainder
|
|
466
|
+
tmpV.fill(0, rows);
|
|
467
|
+
tmpD.fill(0, rows);
|
|
468
|
+
transpose32(tmpV);
|
|
469
|
+
transpose32(tmpD);
|
|
470
|
+
for (let i = 0; i < 32; i++) {
|
|
471
|
+
const dstY = bx * 32 + i;
|
|
472
|
+
if (dstY >= width) break;
|
|
473
|
+
const dstPos = dst.wordIndex(by, dstY);
|
|
474
|
+
const curMask = by >>> 5 === dstStride - 1 ? dstTail : 0xffffffff;
|
|
475
|
+
dstValue[dstPos] = tmpV[i] & curMask;
|
|
476
|
+
dstDefined[dstPos] = tmpD[i] & curMask;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return dst;
|
|
481
|
+
}
|
|
482
|
+
// black <-> white (inplace)
|
|
483
|
+
negate(): Bitmap {
|
|
484
|
+
const n = this.defined.length;
|
|
485
|
+
for (let i = 0; i < n; i++) {
|
|
486
|
+
this.value[i] = ~this.value[i];
|
|
487
|
+
this.defined[i] = 0xffffffff;
|
|
488
|
+
}
|
|
489
|
+
return this;
|
|
282
490
|
}
|
|
283
491
|
// Each pixel size is multiplied by factor
|
|
284
492
|
scale(factor: number): Bitmap {
|
|
@@ -286,37 +494,138 @@ export class Bitmap {
|
|
|
286
494
|
throw new Error(`invalid scale factor: ${factor}`);
|
|
287
495
|
const { height, width } = this;
|
|
288
496
|
const res = new Bitmap({ height: factor * height, width: factor * width });
|
|
289
|
-
return res.rect(
|
|
290
|
-
|
|
291
|
-
Infinity,
|
|
292
|
-
({ x, y }) => this.data[Math.floor(y / factor)][Math.floor(x / factor)]
|
|
497
|
+
return res.rect({ x: 0, y: 0 }, Infinity, ({ x, y }) =>
|
|
498
|
+
this.get((x / factor) | 0, (y / factor) | 0)
|
|
293
499
|
);
|
|
294
500
|
}
|
|
295
501
|
clone(): Bitmap {
|
|
296
502
|
const res = new Bitmap(this.size());
|
|
297
|
-
|
|
503
|
+
res.defined.set(this.defined);
|
|
504
|
+
res.value.set(this.value);
|
|
505
|
+
return res;
|
|
298
506
|
}
|
|
299
507
|
// Ensure that there is no undefined values left
|
|
300
508
|
assertDrawn(): void {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
509
|
+
const { height, width, defined, tailMask, fullWords, words } = this;
|
|
510
|
+
if (!height || !width) return;
|
|
511
|
+
for (let y = 0; y < height; y++) {
|
|
512
|
+
const rowBase = y * words;
|
|
513
|
+
for (let wi = 0; wi < fullWords; wi++) {
|
|
514
|
+
if (defined[rowBase + wi] !== 0xffffffff) throw new Error(`Invalid color type=undefined`);
|
|
515
|
+
}
|
|
516
|
+
if (words !== fullWords && (defined[rowBase + fullWords] & tailMask) !== tailMask)
|
|
517
|
+
throw new Error(`Invalid color type=undefined`);
|
|
518
|
+
}
|
|
304
519
|
}
|
|
305
|
-
|
|
520
|
+
countPatternInRow(y: number, patternLen: number, ...patterns: number[]): number {
|
|
521
|
+
if (patternLen <= 0 || patternLen >= 32) throw new Error('wrong patternLen');
|
|
522
|
+
const mask = (1 << patternLen) - 1;
|
|
523
|
+
const { width, value, words } = this;
|
|
524
|
+
let count = 0;
|
|
525
|
+
const rowBase = this.wordIndex(0, y);
|
|
526
|
+
for (let i = 0, window = 0; i < words; i++) {
|
|
527
|
+
const w = value[rowBase + i];
|
|
528
|
+
const bitEnd = i === words - 1 ? width & 31 || 32 : 32;
|
|
529
|
+
for (let b = 0; b < bitEnd; b++) {
|
|
530
|
+
window = ((window << 1) | ((w >>> b) & 1)) & mask;
|
|
531
|
+
if (i * 32 + b + 1 < patternLen) continue;
|
|
532
|
+
for (const p of patterns) {
|
|
533
|
+
if (window !== p) continue;
|
|
534
|
+
count++;
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return count;
|
|
540
|
+
}
|
|
541
|
+
getRuns(y: number, fn: (len: number, value: boolean) => void): void {
|
|
542
|
+
const { width, value, words } = this;
|
|
543
|
+
if (width === 0) return;
|
|
544
|
+
let runLen = 0;
|
|
545
|
+
let runValue: boolean | undefined;
|
|
546
|
+
const rowBase = this.wordIndex(0, y);
|
|
547
|
+
for (let i = 0; i < words; i++) {
|
|
548
|
+
const word = value[rowBase + i];
|
|
549
|
+
const bitEnd = i === words - 1 ? width & 31 || 32 : 32;
|
|
550
|
+
for (let b = 0; b < bitEnd; b++) {
|
|
551
|
+
const bit = (word & (1 << b)) !== 0;
|
|
552
|
+
if (bit === runValue) {
|
|
553
|
+
runLen++;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (runValue !== undefined) fn(runLen, runValue);
|
|
557
|
+
runValue = bit;
|
|
558
|
+
runLen = 1;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (runValue !== undefined) fn(runLen, runValue);
|
|
562
|
+
}
|
|
563
|
+
popcnt(): number {
|
|
564
|
+
const { height, width, words, fullWords, tailMask } = this;
|
|
565
|
+
if (!height || !width) return 0;
|
|
566
|
+
let count = 0;
|
|
567
|
+
for (let y = 0; y < height; y++) {
|
|
568
|
+
const rowBase = y * words;
|
|
569
|
+
for (let wi = 0; wi < fullWords; wi++) count += popcnt(this.value[rowBase + wi]);
|
|
570
|
+
if (words !== fullWords) count += popcnt(this.value[rowBase + fullWords] & tailMask);
|
|
571
|
+
}
|
|
572
|
+
return count;
|
|
573
|
+
}
|
|
574
|
+
countBoxes2x2(y: number): number {
|
|
575
|
+
const { width, words } = this;
|
|
576
|
+
if (width < 2 || (y | 0) < 0 || y + 1 >= this.height) return 0;
|
|
577
|
+
const base0 = this.wordIndex(0, y) | 0;
|
|
578
|
+
const base1 = this.wordIndex(0, y + 1) | 0;
|
|
579
|
+
// valid "left-edge" positions x in [0 .. W-2]
|
|
580
|
+
const tailBits = width & 31;
|
|
581
|
+
const validLast = tailBits === 0 ? 0x7fffffff : rangeMask(0, (width - 1) & 31);
|
|
582
|
+
let boxes = 0;
|
|
583
|
+
for (let wi = 0; wi < words; wi++) {
|
|
584
|
+
const a0 = this.value[base0 + wi];
|
|
585
|
+
const a1 = this.value[base1 + wi];
|
|
586
|
+
// Compare bit x with bit x+1 at same bit position.
|
|
587
|
+
const eqV = ~(a0 ^ a1) >>> 0; // row0[x] == row1[x]
|
|
588
|
+
const n0 = wi + 1 < words ? this.value[base0 + wi + 1] >>> 0 : 0;
|
|
589
|
+
const eqH0 = ~(a0 ^ (((a0 >>> 1) | ((n0 & 1) << 31)) >>> 0)) >>> 0; // row0[x] == row0[x+1]
|
|
590
|
+
const n1 = wi + 1 < words ? this.value[base1 + wi + 1] >>> 0 : 0;
|
|
591
|
+
const eqH1 = ~(a1 ^ (((a1 >>> 1) | ((n1 & 1) << 31)) >>> 0)) >>> 0; // row1[x] == row1[x+1]
|
|
592
|
+
let m = (eqV & eqH0 & eqH1) >>> 0;
|
|
593
|
+
if (wi === words - 1) m &= validLast;
|
|
594
|
+
boxes += popcnt(m);
|
|
595
|
+
}
|
|
596
|
+
return boxes;
|
|
597
|
+
}
|
|
598
|
+
// Export
|
|
306
599
|
toString(): string {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
600
|
+
const nl = String.fromCharCode(chCodes.newline);
|
|
601
|
+
let out = '';
|
|
602
|
+
for (let y = 0; y < this.height; y++) {
|
|
603
|
+
let line = '';
|
|
604
|
+
for (let x = 0; x < this.width; x++) {
|
|
605
|
+
const v = this.get(x, y);
|
|
606
|
+
line += !this.isDefined(x, y) ? '?' : v ? 'X' : ' ';
|
|
607
|
+
}
|
|
608
|
+
out += line + (y + 1 === this.height ? '' : nl);
|
|
609
|
+
}
|
|
610
|
+
return out;
|
|
611
|
+
}
|
|
612
|
+
toRaw(): DrawValue[][] {
|
|
613
|
+
const out: DrawValue[][] = Array.from({ length: this.height }, () => new Array(this.width));
|
|
614
|
+
for (let y = 0; y < this.height; y++) {
|
|
615
|
+
const row = out[y];
|
|
616
|
+
for (let x = 0; x < this.width; x++) row[x] = this.get(x, y);
|
|
617
|
+
}
|
|
618
|
+
return out;
|
|
310
619
|
}
|
|
311
620
|
toASCII(): string {
|
|
312
|
-
const { height, width
|
|
621
|
+
const { height, width } = this;
|
|
313
622
|
let out = '';
|
|
314
623
|
// Terminal character height is x2 of character width, so we process two rows of bitmap
|
|
315
624
|
// to produce one row of ASCII
|
|
316
625
|
for (let y = 0; y < height; y += 2) {
|
|
317
626
|
for (let x = 0; x < width; x++) {
|
|
318
|
-
const first =
|
|
319
|
-
const second = y + 1 >= height ? true :
|
|
627
|
+
const first = this.get(x, y);
|
|
628
|
+
const second = y + 1 >= height ? true : this.get(x, y + 1); // if last row outside bitmap, make it black
|
|
320
629
|
if (!first && !second)
|
|
321
630
|
out += '█'; // both rows white (empty)
|
|
322
631
|
else if (!first && second)
|
|
@@ -334,9 +643,16 @@ export class Bitmap {
|
|
|
334
643
|
const reset = cc + '[0m';
|
|
335
644
|
const whiteBG = cc + '[1;47m ' + reset;
|
|
336
645
|
const darkBG = cc + `[40m ` + reset;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
646
|
+
const nl = String.fromCharCode(chCodes.newline);
|
|
647
|
+
let out = '';
|
|
648
|
+
for (let y = 0; y < this.height; y++) {
|
|
649
|
+
for (let x = 0; x < this.width; x++) {
|
|
650
|
+
const v = this.get(x, y); // undefined -> white
|
|
651
|
+
out += v ? darkBG : whiteBG;
|
|
652
|
+
}
|
|
653
|
+
out += nl;
|
|
654
|
+
}
|
|
655
|
+
return out;
|
|
340
656
|
}
|
|
341
657
|
toSVG(optimize = true): string {
|
|
342
658
|
let out = `<svg viewBox="0 0 ${this.width} ${this.height}" xmlns="http://www.w3.org/2000/svg">`;
|
|
@@ -407,7 +723,7 @@ export class Bitmap {
|
|
|
407
723
|
let i = 0;
|
|
408
724
|
for (let y = 0; y < height; y++) {
|
|
409
725
|
for (let x = 0; x < width; x++) {
|
|
410
|
-
const value =
|
|
726
|
+
const value = this.get(x, y) ? 0 : 255; // undefined -> white
|
|
411
727
|
data[i++] = value;
|
|
412
728
|
data[i++] = value;
|
|
413
729
|
data[i++] = value;
|
|
@@ -753,8 +1069,8 @@ function interleave(ver: Version, ecc: ErrorCorrection): Coder<Uint8Array, Uint8
|
|
|
753
1069
|
eccBlocks.push(rs.encode(bytes.subarray(0, len)));
|
|
754
1070
|
bytes = bytes.subarray(len);
|
|
755
1071
|
}
|
|
756
|
-
const resBlocks = interleaveBytes(
|
|
757
|
-
const resECC = interleaveBytes(
|
|
1072
|
+
const resBlocks = interleaveBytes(blocks);
|
|
1073
|
+
const resECC = interleaveBytes(eccBlocks);
|
|
758
1074
|
const res = new Uint8Array(resBlocks.length + resECC.length);
|
|
759
1075
|
res.set(resBlocks);
|
|
760
1076
|
res.set(resECC, resBlocks.length);
|
|
@@ -814,29 +1130,29 @@ function drawTemplate(
|
|
|
814
1130
|
const alignPos = info.alignmentPatterns(ver);
|
|
815
1131
|
for (const y of alignPos) {
|
|
816
1132
|
for (const x of alignPos) {
|
|
817
|
-
if (b.
|
|
1133
|
+
if (b.isDefined(x, y)) continue;
|
|
818
1134
|
b.embed({ x: x - 2, y: y - 2 }, align); // center of pattern should be at position
|
|
819
1135
|
}
|
|
820
1136
|
}
|
|
821
1137
|
// Timing patterns
|
|
822
1138
|
b = b
|
|
823
|
-
.hLine({ x: 0, y: 6 }, Infinity, ({ x }
|
|
824
|
-
.vLine({ x: 6, y: 0 }, Infinity, ({ y }
|
|
1139
|
+
.hLine({ x: 0, y: 6 }, Infinity, ({ x }) => (b.isDefined(x, 6) ? undefined : x % 2 == 0))
|
|
1140
|
+
.vLine({ x: 6, y: 0 }, Infinity, ({ y }) => (b.isDefined(6, y) ? undefined : y % 2 == 0));
|
|
825
1141
|
// Format information
|
|
826
1142
|
{
|
|
827
1143
|
const bits = info.formatBits(ecc, maskIdx);
|
|
828
1144
|
const getBit = (i: number) => !test && ((bits >> i) & 1) == 1;
|
|
829
1145
|
// vertical
|
|
830
|
-
for (let i = 0; i < 6; i++) b.
|
|
1146
|
+
for (let i = 0; i < 6; i++) b.set(8, i, getBit(i)); // right of top-left finder
|
|
831
1147
|
// TODO: re-write as lines, like:
|
|
832
1148
|
// b.vLine({ x: 8, y: 0 }, 6, ({ x, y }) => getBit(y));
|
|
833
|
-
for (let i = 6; i < 8; i++) b.
|
|
834
|
-
for (let i = 8; i < 15; i++) b.
|
|
1149
|
+
for (let i = 6; i < 8; i++) b.set(8, i + 1, getBit(i)); // after timing pattern
|
|
1150
|
+
for (let i = 8; i < 15; i++) b.set(8, size - 15 + i, getBit(i)); // right of bottom-left finder
|
|
835
1151
|
// horizontal
|
|
836
|
-
for (let i = 0; i < 8; i++) b.
|
|
837
|
-
for (let i = 8; i < 9; i++) b.
|
|
838
|
-
for (let i = 9; i < 15; i++) b.
|
|
839
|
-
b.
|
|
1152
|
+
for (let i = 0; i < 8; i++) b.set(size - i - 1, 8, getBit(i)); // under top-right finder
|
|
1153
|
+
for (let i = 8; i < 9; i++) b.set(15 - i - 1 + 1, 8, getBit(i)); // VVV, after timing
|
|
1154
|
+
for (let i = 9; i < 15; i++) b.set(15 - i - 1, 8, getBit(i)); // under top-left finder
|
|
1155
|
+
b.set(8, size - 8, !test); // bottom-left finder, right
|
|
840
1156
|
}
|
|
841
1157
|
// Version information
|
|
842
1158
|
if (ver >= 7) {
|
|
@@ -846,8 +1162,8 @@ function drawTemplate(
|
|
|
846
1162
|
const x = Math.floor(i / 3);
|
|
847
1163
|
const y = (i % 3) + size - 8 - 3;
|
|
848
1164
|
// two copies
|
|
849
|
-
b.
|
|
850
|
-
b.
|
|
1165
|
+
b.set(y, x, bit);
|
|
1166
|
+
b.set(x, y, bit);
|
|
851
1167
|
}
|
|
852
1168
|
}
|
|
853
1169
|
return b;
|
|
@@ -869,7 +1185,7 @@ function zigzag(
|
|
|
869
1185
|
for (; ; y += dir) {
|
|
870
1186
|
for (let j = 0; j < 2; j += 1) {
|
|
871
1187
|
const x = xOffset - j;
|
|
872
|
-
if (tpl.
|
|
1188
|
+
if (tpl.isDefined(x, y)) continue; // skip already written elements
|
|
873
1189
|
fn(x, y, pattern(x, y));
|
|
874
1190
|
}
|
|
875
1191
|
if (y + dir < 0 || y + dir >= size) break;
|
|
@@ -901,7 +1217,13 @@ export function utf8ToBytes(str: string): Uint8Array {
|
|
|
901
1217
|
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
902
1218
|
}
|
|
903
1219
|
|
|
904
|
-
function encode(
|
|
1220
|
+
function encode(
|
|
1221
|
+
ver: Version,
|
|
1222
|
+
ecc: ErrorCorrection,
|
|
1223
|
+
data: string,
|
|
1224
|
+
type: EncodingType,
|
|
1225
|
+
encoder: (value: string) => Uint8Array = utf8ToBytes
|
|
1226
|
+
): Uint8Array {
|
|
905
1227
|
let encoded = '';
|
|
906
1228
|
let dataLen = data.length;
|
|
907
1229
|
if (type === 'numeric') {
|
|
@@ -919,7 +1241,7 @@ function encode(ver: Version, ecc: ErrorCorrection, data: string, type: Encoding
|
|
|
919
1241
|
for (let i = 0; i < n - 1; i += 2) encoded += bin(t[i] * 45 + t[i + 1], 11);
|
|
920
1242
|
if (n % 2 == 1) encoded += bin(t[n - 1], 6); // pad if odd number of chars
|
|
921
1243
|
} else if (type === 'byte') {
|
|
922
|
-
const utf8 =
|
|
1244
|
+
const utf8 = encoder(data);
|
|
923
1245
|
dataLen = utf8.length;
|
|
924
1246
|
encoded = Array.from(utf8)
|
|
925
1247
|
.map((i) => bin(i, 8))
|
|
@@ -961,70 +1283,60 @@ function drawQR(
|
|
|
961
1283
|
value = ((data[i >>> 3] >> ((7 - i) & 7)) & 1) !== 0;
|
|
962
1284
|
i++;
|
|
963
1285
|
}
|
|
964
|
-
b.
|
|
1286
|
+
b.set(x, y, value !== mask); // !== as xor
|
|
965
1287
|
});
|
|
966
1288
|
if (i !== need) throw new Error('QR: bytes left after draw');
|
|
967
1289
|
return b;
|
|
968
1290
|
}
|
|
969
1291
|
|
|
1292
|
+
const mkPattern = (pattern: boolean[]) => {
|
|
1293
|
+
const s = pattern.map((i) => (i ? '1' : '0')).join('');
|
|
1294
|
+
return { len: s.length, n: Number(`0b${s}`) };
|
|
1295
|
+
};
|
|
1296
|
+
// 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column, preceded or followed by light area 4 modules wide
|
|
1297
|
+
const finderPattern = [true, false, true, true, true, false, true]; // dark:light:dark:light:dark
|
|
1298
|
+
const lightPattern = [false, false, false, false]; // light area 4 modules wide
|
|
1299
|
+
const P1 = mkPattern([...finderPattern, ...lightPattern]);
|
|
1300
|
+
const P2 = mkPattern([...lightPattern, ...finderPattern]);
|
|
1301
|
+
|
|
970
1302
|
function penalty(bm: Bitmap): number {
|
|
971
|
-
const
|
|
1303
|
+
const { width, height } = bm;
|
|
1304
|
+
const transposed = bm.transpose();
|
|
972
1305
|
// Adjacent modules in row/column in same | No. of modules = (5 + i) color
|
|
973
|
-
const sameColor = (row: DrawValue[]) => {
|
|
974
|
-
let res = 0;
|
|
975
|
-
for (let i = 0, same = 1, last = undefined; i < row.length; i++) {
|
|
976
|
-
if (last === row[i]) {
|
|
977
|
-
same++;
|
|
978
|
-
if (i !== row.length - 1) continue; // handle last element
|
|
979
|
-
}
|
|
980
|
-
if (same >= 5) res += 3 + (same - 5);
|
|
981
|
-
last = row[i];
|
|
982
|
-
same = 1;
|
|
983
|
-
}
|
|
984
|
-
return res;
|
|
985
|
-
};
|
|
986
1306
|
let adjacent = 0;
|
|
987
|
-
|
|
988
|
-
|
|
1307
|
+
for (let y = 0; y < height; y++) {
|
|
1308
|
+
bm.getRuns(y, (len) => {
|
|
1309
|
+
if (len >= 5) adjacent += 3 + (len - 5);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
for (let y = 0; y < width; y++) {
|
|
1313
|
+
transposed.getRuns(y, (len) => {
|
|
1314
|
+
if (len >= 5) adjacent += 3 + (len - 5);
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
989
1317
|
// Block of modules in same color (Block size = 2x2)
|
|
990
1318
|
let box = 0;
|
|
991
|
-
let
|
|
992
|
-
|
|
993
|
-
const lastH = bm.height - 1;
|
|
994
|
-
for (let x = 0; x < lastW; x++) {
|
|
995
|
-
for (let y = 0; y < lastH; y++) {
|
|
996
|
-
const x1 = x + 1;
|
|
997
|
-
const y1 = y + 1;
|
|
998
|
-
if (b[x][y] === b[x1][y] && b[x1][y] === b[x][y1] && b[x1][y] === b[x1][y1]) {
|
|
999
|
-
box += 3;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
// 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column, preceded or followed by light area 4 modules wide
|
|
1004
|
-
const finderPattern = (row: DrawValue[]) => {
|
|
1005
|
-
const finderPattern = [true, false, true, true, true, false, true]; // dark:light:dark:light:dark
|
|
1006
|
-
const lightPattern = [false, false, false, false]; // light area 4 modules wide
|
|
1007
|
-
const p1 = [...finderPattern, ...lightPattern];
|
|
1008
|
-
const p2 = [...lightPattern, ...finderPattern];
|
|
1009
|
-
let res = 0;
|
|
1010
|
-
for (let i = 0; i < row.length; i++) {
|
|
1011
|
-
if (includesAt(row, p1, i)) res += 40;
|
|
1012
|
-
if (includesAt(row, p2, i)) res += 40;
|
|
1013
|
-
}
|
|
1014
|
-
return res;
|
|
1015
|
-
};
|
|
1319
|
+
for (let y = 0; y < height - 1; y++) box += 3 * bm.countBoxes2x2(y);
|
|
1320
|
+
|
|
1016
1321
|
let finder = 0;
|
|
1017
|
-
for (
|
|
1018
|
-
for (
|
|
1322
|
+
for (let y = 0; y < height; y++) finder += 40 * bm.countPatternInRow(y, P1.len, P1.n, P2.n);
|
|
1323
|
+
for (let y = 0; y < width; y++)
|
|
1324
|
+
finder += 40 * transposed.countPatternInRow(y, P1.len, P1.n, P2.n);
|
|
1325
|
+
|
|
1019
1326
|
// Proportion of dark modules in entire symbol
|
|
1020
1327
|
// Add 10 points to a deviation of 5% increment or decrement in the proportion
|
|
1021
1328
|
// ratio of dark module from the referential 50%
|
|
1022
1329
|
let darkPixels = 0;
|
|
1023
|
-
bm.
|
|
1024
|
-
|
|
1330
|
+
darkPixels = bm.popcnt();
|
|
1331
|
+
//bm.rectRead(0, Infinity, (_c, val) => (darkPixels += val ? 1 : 0));
|
|
1332
|
+
// for (let y = 0; y < height; y++) {
|
|
1333
|
+
// for (let x = 0; x < width; x++) if (bm.get(x, y)) darkPixels++;
|
|
1334
|
+
// }
|
|
1335
|
+
const darkPercent = (darkPixels / (height * width)) * 100;
|
|
1025
1336
|
const dark = 10 * Math.floor(Math.abs(darkPercent - 50) / 5);
|
|
1026
1337
|
return adjacent + box + finder + dark;
|
|
1027
1338
|
}
|
|
1339
|
+
|
|
1028
1340
|
// Selects best mask according to penalty, if no mask is provided
|
|
1029
1341
|
function drawQRBest(ver: Version, ecc: ErrorCorrection, data: Uint8Array, maskIdx?: Mask) {
|
|
1030
1342
|
if (maskIdx === undefined) {
|
|
@@ -1041,6 +1353,7 @@ function drawQRBest(ver: Version, ecc: ErrorCorrection, data: Uint8Array, maskId
|
|
|
1041
1353
|
export type QrOpts = {
|
|
1042
1354
|
ecc?: ErrorCorrection | undefined;
|
|
1043
1355
|
encoding?: EncodingType | undefined;
|
|
1356
|
+
textEncoder?: (text: string) => Uint8Array;
|
|
1044
1357
|
version?: Version | undefined;
|
|
1045
1358
|
mask?: number | undefined;
|
|
1046
1359
|
border?: number | undefined;
|
|
@@ -1112,13 +1425,13 @@ export function encodeQR(text: string, output: Output = 'raw', opts: QrOpts & Sv
|
|
|
1112
1425
|
err = new Error('Unknown error');
|
|
1113
1426
|
if (ver !== undefined) {
|
|
1114
1427
|
validateVersion(ver);
|
|
1115
|
-
data = encode(ver, ecc, text, encoding);
|
|
1428
|
+
data = encode(ver, ecc, text, encoding, opts.textEncoder);
|
|
1116
1429
|
} else {
|
|
1117
1430
|
// If no version is provided, try to find smallest one which fits
|
|
1118
1431
|
// Currently just scans all version, can be significantly speedup if needed
|
|
1119
1432
|
for (let i = 1; i <= 40; i++) {
|
|
1120
1433
|
try {
|
|
1121
|
-
data = encode(i, ecc, text, encoding);
|
|
1434
|
+
data = encode(i, ecc, text, encoding, opts.textEncoder);
|
|
1122
1435
|
ver = i;
|
|
1123
1436
|
break;
|
|
1124
1437
|
} catch (e) {
|
|
@@ -1133,7 +1446,7 @@ export function encodeQR(text: string, output: Output = 'raw', opts: QrOpts & Sv
|
|
|
1133
1446
|
if (!Number.isSafeInteger(border)) throw new Error(`invalid border type=${typeof border}`);
|
|
1134
1447
|
res = res.border(border, false); // Add border
|
|
1135
1448
|
if (opts.scale !== undefined) res = res.scale(opts.scale); // Scale image
|
|
1136
|
-
if (output === 'raw') return res.
|
|
1449
|
+
if (output === 'raw') return res.toRaw();
|
|
1137
1450
|
else if (output === 'ascii') return res.toASCII();
|
|
1138
1451
|
else if (output === 'svg') return res.toSVG(opts.optimize);
|
|
1139
1452
|
else if (output === 'gif') return res.toGIF();
|
|
@@ -1146,6 +1459,7 @@ export default encodeQR;
|
|
|
1146
1459
|
export const utils: {
|
|
1147
1460
|
best: typeof best;
|
|
1148
1461
|
bin: typeof bin;
|
|
1462
|
+
popcnt: typeof popcnt;
|
|
1149
1463
|
drawTemplate: typeof drawTemplate;
|
|
1150
1464
|
fillArr: typeof fillArr;
|
|
1151
1465
|
info: {
|
|
@@ -1191,6 +1505,7 @@ export const utils: {
|
|
|
1191
1505
|
} = {
|
|
1192
1506
|
best,
|
|
1193
1507
|
bin,
|
|
1508
|
+
popcnt,
|
|
1194
1509
|
drawTemplate,
|
|
1195
1510
|
fillArr,
|
|
1196
1511
|
info,
|