18
loading...
This website collects cookies to deliver better user experience
https://www.qrcode.com/
), the first codeword is 65, or 01000001 in binary, so the first module will report 0, not 1, and thus it will be light; the second one will be dark, then 5 more light modules, and finally a dark module.<canvas>
, a bunch of square <span>
s, or even these two emojis: ⬜⬛. It's not really important, nor difficult for someone with a minimal expertise in rendering stuff on the web.Uint8Array
s again, because they're faster than regular arrays and also for the .set()
method that comes in handy. We'll start simple:function getSize(version) {
return version * 4 + 17;
}
function getNewMatrix(version) {
const length = getSize(version);
return Array.from({ length }, () => new Uint8Array(length));
}
Array.from
is basically a map
function that lets us using a new typed array for each row (i.e., new Array(length).fill(new Uint8Array(length))
would use the same array for each row).function fillArea(matrix, row, column, width, height, fill = 1) {
const fillRow = new Uint8Array(width).fill(fill);
for (let index = row; index < row + height; index++) {
// YES, this mutates the matrix. Watch out!
matrix[index].set(fillRow, column);
}
}
function getModuleSequence(version) {
const matrix = getNewMatrix(version);
const size = getSize(version);
// Finder patterns + divisors
fillArea(matrix, 0, 0, 9, 9);
fillArea(matrix, 0, size - 8, 8, 9);
fillArea(matrix, size - 8, 0, 9, 8);
// Alignment pattern - yes, we just place one. For the general
// implementation, wait for the next parts in the series!
fillArea(matrix, size - 9, size - 9, 5, 5);
// Timing patterns
fillArea(matrix, 6, 9, version * 4, 1);
fillArea(matrix, 9, 6, 1, version * 4);
// Dark module
matrix[size - 8][8] = 1;
let rowStep = -1;
let row = size - 1;
let column = size - 1;
const sequence = [];
let index = 0;
while (column >= 0) {
if (matrix[row][column] === 0) {
sequence.push([row, column]);
}
// Checking the parity of the index of the current module
if (index & 1) {
row += rowStep;
if (row === -1 || row === size) {
rowStep = -rowStep;
row += rowStep;
column -= column === 7 ? 2 : 1;
} else {
column++;
}
} else {
column--;
}
index++;
}
return sequence;
}
rowStep
to track whether we're going upwards or downwards in the matrix. Then we're using index
and its parity to determine if we need to go left, or move diagonally.getModuleSequence(2)
// Uint8Array(359) [[24, 24], [24, 23], [23, 24], ..., [16, 0]]
function getRawQRCode(message) {
// One day, we'll compute these values. But not today!
const VERSION = 2;
const TOTAL_CODEWORDS = 44;
const LENGTH_BITS = 8;
const DATA_CODEWORDS = 28;
const codewords = new Uint8Array(TOTAL_CODEWORDS);
codewords.set(getByteData(message, LENGTH_BITS, DATA_CODEWORDS), 0);
codewords.set(getEDC(byteData, TOTAL_CODEWORDS), DATA_CODEWORDS);
const size = getSize(VERSION);
const qrCode = getNewMatrix(VERSION);
const moduleSequence = getModuleSequence(VERSION);
// Placing the fixed patterns
// Finder patterns
[[0, 0], [size - 7, 0], [0, size - 7]].forEach(([row, col]) => {
fillArea(qrCode, row, col, 7, 7);
fillArea(qrCode, row + 1, col + 1, 5, 5, 0);
fillArea(qrCode, row + 2, col + 2, 3, 3);
});
// Separators
fillArea(qrCode, 7, 0, 8, 1, 0);
fillArea(qrCode, 0, 7, 1, 7, 0);
fillArea(qrCode, size - 8, 0, 8, 1, 0);
fillArea(qrCode, 0, size - 8, 1, 7, 0);
fillArea(qrCode, 7, size - 8, 8, 1, 0);
fillArea(qrCode, size - 7, 7, 1, 7, 0);
// Alignment pattern
fillArea(qrCode, size - 9, size - 9, 5, 5);
fillArea(qrCode, size - 8, size - 8, 3, 3, 0);
qrCode[size - 7][size - 7] = 1;
// Timing patterns
for (let pos = 8; pos < VERSION * 4 + 8; pos += 2) {
qrCode[6][pos] = 1;
qrCode[6][pos + 1] = 0;
qrCode[pos][6] = 1;
qrCode[pos + 1][6] = 0;
}
qrCode[6][size - 7] = 1;
qrCode[size - 7][6] = 1;
// Dark module
qrCode[size - 8][8] = 1;
// Placing message and error data
let index = 0;
for (const codeword of codewords) {
// Counting down from the leftmost bit
for (let shift = 7; shift >= 0; shift--;) {
const bit = (codeword >> shift) & 1;
const [row, column] = moduleSequence[index];
index++;
qrCode[row][column] = bit;
}
}
return qrCode;
}