qrazybox/js/qr.js
2022-01-02 15:36:22 +01:00

263 lines
7.2 KiB
JavaScript

const WHITE_COLOR = 0;
const BLACK_COLOR = 1;
const RED_COLOR = 3;
const GREY_COLOR = -1;
/*
A finder is defined by 3 squares:
- A black 7x7 square
- A white 5x5 square
- A black 3x3 square
Padding is denoted by a 9x9 square surrounding the element
and being cropped on its corners.
x and y denote the (x,y) coordinate of the 7x7 square, taken
at the top left corner
*/
function draw_square(t, x, y, size, color){
for(let i=x; i<x+size; i++){
for(let j=y; j<y+size; j++){
if(i >= t.length || i < 0){
continue
}
if(j >= t.length || j < 0) {
continue
}
t[i][j] = color;
}
}
}
function generate_finder_separator(t, x, y) {
return draw_square(t, x - 1, y - 1, 9, WHITE_COLOR);
}
function generate_finder(t, x, y){
generate_finder_separator(t, x, y);
draw_square(t, x, y, 7, BLACK_COLOR);
draw_square(t, x + 1, y + 1, 5, WHITE_COLOR);
draw_square(t, x + 2, y + 2, 3, BLACK_COLOR);
}
/*
The timing patterns is a pattern of black / white squares (1x1) that
begin with a black one and move either horizontally or vertically,
connecting the finders separators.
*/
function generate_timing_pattern_v(t, xStart, yStart, yEnd){
// Start with black
let current = BLACK_COLOR;
for(let j=yStart; j<=yEnd; j++){
t[xStart][j] = current;
current = (current == BLACK_COLOR ? WHITE_COLOR : BLACK_COLOR)
}
}
function generate_timing_pattern_h(t, xStart, yStart, xEnd){
// Start with black
let current = BLACK_COLOR;
for(let i=xStart; i<=xEnd; i++){
t[i][yStart] = current;
current = (current == BLACK_COLOR ? WHITE_COLOR : BLACK_COLOR)
}
}
let versions_size = [];
for(let i=0;i<40;i++){
versions_size.push(
[21 + i*4, 21 + i*4]
)
}
let alignment_lookup = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170]
];
/*
Alignment patterns are boxes sized 5x5 that are
formed as follows:
- Outer 5x5 black square
- Inner 3x3 white square
- Inner-most 1x1 black square
The coordinates from alignment_lookup refer to the center
of the alignment pattern (the 1x1 square).
*/
function add_alignment_patterns(t, index){
let alignment = alignment_lookup[index];
if(alignment.length == 0){
return;
}
let alignment_locs = [];
for(let i=0; i<alignment.length; i++){
for(let j=0; j<alignment.length; j++){
alignment_locs.push([alignment[i], alignment[j]]);
}
}
for(let align of alignment_locs){
let [x, y] = [align[0], align[1]];
let [l_x, r_x] = [x-2, x+2];
let [t_y, b_y] = [y-2, y+2];
if(r_x > t.length - 8 && t_y < 8) {
continue
}
if(l_x < 8 && t_y < 8) {
continue;
}
if(l_x < 8 && b_y > t.length - 8){
continue
}
draw_square(t, x-2, y-2, 5, BLACK_COLOR);
draw_square(t, x-1, y-1, 3, WHITE_COLOR);
draw_square(t, x, y, 1, BLACK_COLOR);
}
console.log(alignment_locs);
}
// from table.js version_information_table ( left most bit is still position 17 )
var version_information_table_bit = [
[0,0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0], //7
[0,0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0], //8
[0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1], //9
[0,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1], //10
[0,0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0], //11
[0,0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0], //12
[0,0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1], //13
[0,0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1], //14
[0,0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0], //15
[0,1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0], //16
[0,1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1], //17
[0,1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1], //18
[0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0], //19
[0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,1,1,0], //20
[0,1,0,1,0,1,0,1,1,0,1,0,0,0,0,0,1,1], //21
[0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0,0,1], //22
[0,1,0,1,1,1,0,1,1,1,1,1,1,0,1,1,0,0], //23
[0,1,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0], //24
[0,1,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,1], //25
[0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1], //26
[0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0], //27
[0,1,1,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0], //28
[0,1,1,1,0,1,0,0,1,1,0,0,1,1,1,1,1,1], //29
[0,1,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1], //30
[0,1,1,1,1,1,0,0,1,0,0,1,0,1,0,0,0,0], //31
[1,0,0,0,0,0,1,0,0,1,1,1,0,1,0,1,0,1], //32
[1,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0,0], //33
[1,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,1,0], //34
[1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1], //35
[1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,1,1], //36
[1,0,0,1,0,1,0,1,0,0,0,0,1,0,1,1,1,0], //37
[1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,1,0,0], //38
[1,0,0,1,1,1,0,1,0,1,0,1,0,0,0,0,0,1], //39
[1,0,1,0,0,0,1,1,0,0,0,1,1,0,1,0,0,1] //40
];
function add_version_info(t, version){
if (version >= 7) {
for (step=0 ; step < 3 ; step++ ){
x=0;
for(let i = 17 - step; i>=0 ; i -= 3){
// Bottom Left
draw_square(t, 4*version + 6 + step, x ,1, version_information_table_bit[version - 7][i]);
// Top Right
draw_square(t, x, 4*version + 6 + step ,1, version_information_table_bit[version - 7][i]);
x++;
}
}
}
return t;
}
// https://www.thonky.com/qr-code-tutorial/format-version-information
function add_dark_module(t, version){
//dark module is always (8, 4*version + 9)
draw_square(t, 4*version + 9, 8 ,1, BLACK_COLOR);
return t;
}
function generate_qr(version){
console.log(`Generating ${version}`)
let t = [];
let sizes = versions_size[version-1];
let x_max = sizes[0];
let y_max = sizes[1];
for(let i = 0; i<x_max; i++){
let arr = [];
for(let j=0; j<y_max; j++){
arr.push(GREY_COLOR);
}
t.push(arr);
}
// Add finder
generate_finder(t, 0, 0);
generate_finder(t, x_max - 7, 0);
generate_finder(t, 0, y_max - 7);
generate_timing_pattern_v(t, 6, 8, y_max-9);
generate_timing_pattern_h(t, 8, 6, x_max-9);
add_alignment_patterns(t, version-1);
add_dark_module(t,version);
add_version_info(t,version);
return t;
}