wip: support more QR code versions

This commit is contained in:
Denys Vitali 2021-11-14 23:56:31 +01:00
parent 12df65b9d6
commit 6fb5b55863
5 changed files with 345 additions and 5 deletions

61
finder-example.html Normal file
View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>QR Code Finder Example</title>
<meta charset="utf-8" />
<style>
#links > a {
display: inline-block;
margin: 5px;
}
</style>
</head>
<body>
<div id="links"></div>
<canvas id="canvas-1" width="800" height="800"></canvas>
<script src="js/qr.js"></script>
<script type="text/javascript">
let links = document.getElementById('links');
for(let i=0; i<20; i++) {
let link = document.createElement('a');
link.href = "#" + (i + 1);
link.text = i+1;
links.appendChild(link);
}
let canvas = document.getElementById("canvas-1");
let ctx = canvas.getContext("2d");
window.addEventListener("hashchange", refreshQR);
function refreshQR() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let fragment = window.location.hash.substr(1);
let qrCodeVersion = 1;
if (fragment != "") {
qrCodeVersion = fragment * 1;
}
let wsize = 10;
let hsize = 10;
let qrTemplate = generate_qr(qrCodeVersion);
for (let i = 0; i < qrTemplate.length; i++) {
for (let j = 0; j < qrTemplate[0].length; j++) {
// Drawing (i, j)
let v = qrTemplate[i][j];
if (v == -1) {
v = 2;
}
let colorArr = ["white", "black", "grey"];
let color = colorArr[v];
ctx.fillStyle = color;
ctx.fillRect(i * wsize, j * hsize, wsize, hsize);
}
}
}
refreshQR();
</script>
</body>
</html>

61
index2.html Normal file
View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html>
<head>
<title>QR Code Display</title>
<meta charset="utf-8" />
<style>
#links > a {
display: inline-block;
margin: 5px;
}
</style>
</head>
<body>
<div id="links"></div>
<canvas id="canvas-1" width="800" height="800"></canvas>
<script src="js/table.js"></script>
<script type="text/javascript">
let links = document.getElementById('links');
for(let i=0; i<20; i++) {
let link = document.createElement('a');
link.href = "#" + (i + 1);
link.text = i+1;
links.appendChild(link);
}
let canvas = document.getElementById("canvas-1");
let ctx = canvas.getContext("2d");
window.addEventListener("hashchange", refreshQR);
function refreshQR() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let fragment = window.location.hash.substr(1);
let qrCodeVersion = 1;
if (fragment != "") {
qrCodeVersion = fragment * 1;
}
let wsize = 10;
let hsize = 10;
let qrTemplate = qr_templates[qrCodeVersion - 1];
for (let i = 0; i < qrTemplate.length; i++) {
for (let j = 0; j < qrTemplate[0].length; j++) {
// Drawing (i, j)
let v = qrTemplate[i][j];
if (v == -1) {
v = 2;
}
let colorArr = ["white", "black", "grey", "red"];
let color = colorArr[v];
ctx.fillStyle = color;
ctx.fillRect(i * wsize, j * hsize, wsize, hsize);
}
}
}
refreshQR();
</script>
</body>
</html>

View file

@ -6,7 +6,7 @@
****************************************
*/
var APP_VERSION = '0.3.3';
var APP_VERSION = '0.4.0';
var qr_version = 1; //Current QR version (1-9)
var qr_pixel_size = 10; //Current view size of QR code (pixel per module)
@ -33,6 +33,9 @@ var is_data_module = []; //Store data that separate between data module a
var history_array = []; //Store history information and its qr_array data
var active_history = -1; //Current active history
const maxSupportedSize = 100;
const maxVersion = 50;
/***
*
* generate QR table based on qr_array
@ -571,7 +574,7 @@ function importFromImage(src, cb){
var qrArray = qRCodeMatrix.bits.bits;
var size = qRCodeMatrix.bits.width;
if(size > 53){
if(size > maxSupportedSize){
alert("QR version is unsupported");
return;
}
@ -1462,7 +1465,7 @@ $(document).ready(function(){
$("#btn-version-plus").click(function(){
if(changed_state){
if(confirm("Are you sure want to proceed?\nYour unsaved progress will be lost!")){
if(qr_version != 9){
if(qr_version != maxVersion){
qr_version += 1;
qr_size = 17+(qr_version*4);
$("#qr-version").val(qr_size+"x"+qr_size+" (ver. "+qr_version+")");
@ -1470,7 +1473,7 @@ $(document).ready(function(){
}
}
} else {
if(qr_version != 9){
if(qr_version != maxVersion){
qr_version += 1;
qr_size = 17+(qr_version*4);
$("#qr-version").val(qr_size+"x"+qr_size+" (ver. "+qr_version+")");

197
js/qr.js Normal file
View file

@ -0,0 +1,197 @@
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 = [];
for(let i=0;i<40;i++){
versions.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);
}
function generate_qr(version){
console.log(`Generating ${version}`)
let t = [];
let sizes = versions[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);
return t;
}

View file

@ -56,7 +56,25 @@ var alignment_pattern_array = [
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46]
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 36, 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]
];
var format_information_bits_raw = {