qrcode-pure
Advanced tools
Comparing version 0.0.1 to 0.0.2
1738
lib/decode.js
@@ -9,1081 +9,1081 @@ /** | ||
function QRDecode() { | ||
this.image = null; | ||
this.imageTop = 0; | ||
this.imageBottom = 0; | ||
this.imageLeft = 0; | ||
this.imageRight = 0; | ||
this.nModules = 0; | ||
this.moduleSize = 0; | ||
this.version = 0; | ||
this.functionalGrade = 0; | ||
this.ECLevel = 0; | ||
this.mask = 0; | ||
this.maskPattern = []; | ||
this.nBlockEcWords = 0; | ||
this.blockIndices = []; | ||
this.blockDataLengths = []; | ||
this.image = null; | ||
this.imageTop = 0; | ||
this.imageBottom = 0; | ||
this.imageLeft = 0; | ||
this.imageRight = 0; | ||
this.nModules = 0; | ||
this.moduleSize = 0; | ||
this.version = 0; | ||
this.functionalGrade = 0; | ||
this.ECLevel = 0; | ||
this.mask = 0; | ||
this.maskPattern = []; | ||
this.nBlockEcWords = 0; | ||
this.blockIndices = []; | ||
this.blockDataLengths = []; | ||
} | ||
QRDecode.prototype = { | ||
/** | ||
* Decode a pixel array | ||
* @param pix | ||
* @returns {*} | ||
*/ | ||
decodePix: function(pix) { | ||
return this.decodeImage(pix); | ||
}, | ||
/** | ||
* Decode image data as QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
*/ | ||
decodeImageData: function(imageData, imageWidth, imageHeight) { | ||
this.setImageData(imageData, imageWidth, imageHeight); | ||
/** | ||
* Decode a pixel array | ||
* @param pix | ||
* @returns {*} | ||
*/ | ||
decodePix: function (pix) { | ||
return this.decodeImage(pix); | ||
}, | ||
/** | ||
* Decode image data as QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
*/ | ||
decodeImageData: function (imageData, imageWidth, imageHeight) { | ||
this.setImageData(imageData, imageWidth, imageHeight); | ||
return this.decode(); | ||
}, | ||
/** | ||
* Decode image data as QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
* @param left Leftmost pixel of image | ||
* @param right Rightmost pixel of image | ||
* @param top Top pixel of image | ||
* @param bottom Bottom pixel of image | ||
* @param maxVersion Do not try to decode with version higher than this | ||
*/ | ||
decodeImageDataInsideBordersWithMaxVersion: function(imageData, imageWidth, imageHeight, left, right, top, bottom, maxVersion) { | ||
this.setImageData(imageData, imageWidth, imageHeight); | ||
this.imageLeft = left; | ||
this.imageRight = right; | ||
this.imageTop = top; | ||
this.imageBottom = bottom; | ||
this.imageSize = ((this.imageRight - this.imageLeft + 1) + (this.imageBottom - this.imageTop + 1)) / 2.0; | ||
this.maxVersion = maxVersion; | ||
return this.decode(); | ||
}, | ||
/** | ||
* Decode image data as QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
* @param left Leftmost pixel of image | ||
* @param right Rightmost pixel of image | ||
* @param top Top pixel of image | ||
* @param bottom Bottom pixel of image | ||
* @param maxVersion Do not try to decode with version higher than this | ||
*/ | ||
decodeImageDataInsideBordersWithMaxVersion: function (imageData, imageWidth, imageHeight, left, right, top, bottom, maxVersion) { | ||
this.setImageData(imageData, imageWidth, imageHeight); | ||
this.imageLeft = left; | ||
this.imageRight = right; | ||
this.imageTop = top; | ||
this.imageBottom = bottom; | ||
this.imageSize = ((this.imageRight - this.imageLeft + 1) + (this.imageBottom - this.imageTop + 1)) / 2.0; | ||
this.maxVersion = maxVersion; | ||
return this.decodeInsideBordersWithMaxVersion(); | ||
}, | ||
/** | ||
* Set image data in preparation for decoding QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
*/ | ||
setImageData: function(imageData, imageWidth, imageHeight) { | ||
var total = 0, | ||
x, y, p, v; | ||
return this.decodeInsideBordersWithMaxVersion(); | ||
}, | ||
/** | ||
* Set image data in preparation for decoding QR Code | ||
* @param imageData The image data (canvas.getContext('2d').getImageData, pixel array or similar) | ||
* @param imageWidth The pixel width of the image | ||
* @param imageHeight The pixel height of the image | ||
*/ | ||
setImageData: function (imageData, imageWidth, imageHeight) { | ||
var total = 0, | ||
x, y, p, v; | ||
imageData.minCol = 255; | ||
imageData.maxCol = 0; | ||
imageData.minCol = 255; | ||
imageData.maxCol = 0; | ||
for (x = 0; x < imageWidth; x++) { | ||
for (y = 0; y < imageHeight; y++) { | ||
p = x * 4 + y * imageWidth * 4; | ||
v = 0.30 * imageData.data[p] + 0.59 * imageData.data[p + 1] + 0.11 * imageData.data[p + 2]; | ||
total += v; | ||
for (x = 0; x < imageWidth; x++) { | ||
for (y = 0; y < imageHeight; y++) { | ||
p = x * 4 + y * imageWidth * 4; | ||
v = 0.30 * imageData.data[p] + 0.59 * imageData.data[p + 1] + 0.11 * imageData.data[p + 2]; | ||
total += v; | ||
if (v < imageData.minCol) { | ||
imageData.minCol = v; | ||
} | ||
if (v > imageData.maxCol) { | ||
imageData.maxCol = v; | ||
} | ||
} | ||
if (v < imageData.minCol) { | ||
imageData.minCol = v; | ||
} | ||
if (imageData.maxCol - imageData.minCol < 255 / 10) { | ||
throw new QRBase.QRError( | ||
'Image does not have enough contrast (this.image_data.min_col=' + | ||
imageData.minCol + ' this.image_data.max_col=' + imageData.maxCol + ')', | ||
2, { minCol: imageData.minCol, maxCol: imageData.maxCol }); | ||
if (v > imageData.maxCol) { | ||
imageData.maxCol = v; | ||
} | ||
} | ||
} | ||
imageData.threshold = total / (imageWidth * imageHeight); | ||
if (imageData.maxCol - imageData.minCol < 255 / 10) { | ||
throw new QRBase.QRError( | ||
'Image does not have enough contrast (this.image_data.min_col=' + | ||
imageData.minCol + ' this.image_data.max_col=' + imageData.maxCol + ')', | ||
2, { minCol: imageData.minCol, maxCol: imageData.maxCol }); | ||
} | ||
imageData.getGray = function(x, y, d) { | ||
var n = 0, | ||
i, j, p; | ||
imageData.threshold = total / (imageWidth * imageHeight); | ||
for (i = x; i < x + d; i++) { | ||
for (j = y; j < y + d; j++) { | ||
p = i * 4 + j * this.width * 4; | ||
n = n + 0.30 * this.data[p] + 0.59 * this.data[p + 1] + 0.11 * this.data[p + 2]; | ||
} | ||
} | ||
imageData.getGray = function (x, y, d) { | ||
var n = 0, | ||
i, j, p; | ||
return n / d / d; | ||
}; | ||
for (i = x; i < x + d; i++) { | ||
for (j = y; j < y + d; j++) { | ||
p = i * 4 + j * this.width * 4; | ||
n = n + 0.30 * this.data[p] + 0.59 * this.data[p + 1] + 0.11 * this.data[p + 2]; | ||
} | ||
} | ||
imageData.isDark = function(x, y, d) { | ||
var g = this.getGray(x, y, d); | ||
return g < this.threshold; | ||
}; | ||
return n / d / d; | ||
}; | ||
this.image = imageData; | ||
}, | ||
/** | ||
* Decode a QR Code in an image. | ||
* The image MUST already have .getGray set | ||
*/ | ||
decodeImage: function(image) { | ||
this.image = image; | ||
imageData.isDark = function (x, y, d) { | ||
var g = this.getGray(x, y, d); | ||
return g < this.threshold; | ||
}; | ||
return this.decode(); | ||
}, | ||
/** | ||
* Decode a QR Code in an image which has already been set. | ||
*/ | ||
decode: function() { | ||
this.findImageBorders(); | ||
this.maxVersion = 40; | ||
this.decodeInsideBordersWithMaxVersion(); | ||
this.image = imageData; | ||
}, | ||
/** | ||
* Decode a QR Code in an image. | ||
* The image MUST already have .getGray set | ||
*/ | ||
decodeImage: function (image) { | ||
this.image = image; | ||
return this.data; | ||
}, | ||
/** | ||
* Decode a QR Code in an image which has already been set - | ||
* inside borders already defined | ||
*/ | ||
decodeInsideBordersWithMaxVersion: function() { | ||
this.findModuleSize(); | ||
QRBase.setFunctionalPattern(this); | ||
this.extractCodewords(); | ||
QRBase.setBlocks(this); | ||
this.correctErrors(); | ||
this.extractData(); | ||
return this.decode(); | ||
}, | ||
/** | ||
* Decode a QR Code in an image which has already been set. | ||
*/ | ||
decode: function () { | ||
this.findImageBorders(); | ||
this.maxVersion = 40; | ||
this.decodeInsideBordersWithMaxVersion(); | ||
return this.data; | ||
}, | ||
/** | ||
* QRCode internal decoding functions | ||
*/ | ||
findImageBorders: function() { | ||
var i, j, n, | ||
limit = 7, | ||
skewLimit = 2; | ||
return this.data; | ||
}, | ||
/** | ||
* Decode a QR Code in an image which has already been set - | ||
* inside borders already defined | ||
*/ | ||
decodeInsideBordersWithMaxVersion: function () { | ||
this.findModuleSize(); | ||
QRBase.setFunctionalPattern(this); | ||
this.extractCodewords(); | ||
QRBase.setBlocks(this); | ||
this.correctErrors(); | ||
this.extractData(); | ||
for (i = 0; i < this.image.width; i++) { | ||
n = 0; | ||
return this.data; | ||
}, | ||
/** | ||
* QRCode internal decoding functions | ||
*/ | ||
findImageBorders: function () { | ||
var i, j, n, | ||
limit = 7, | ||
skewLimit = 2; | ||
for (j = 0; j < this.image.height; j++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
for (i = 0; i < this.image.width; i++) { | ||
n = 0; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (j = 0; j < this.image.height; j++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
this.imageLeft = i; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (i = this.image.width - 1; i >= 0; i--) { | ||
n = 0; | ||
this.imageLeft = i; | ||
for (j = 0; j < this.image.height; j++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
for (i = this.image.width - 1; i >= 0; i--) { | ||
n = 0; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (j = 0; j < this.image.height; j++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
this.imageRight = i; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (j = 0; j < this.image.height; j++) { | ||
n = 0; | ||
this.imageRight = i; | ||
for (i = 0; i < this.image.width; i++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
for (j = 0; j < this.image.height; j++) { | ||
n = 0; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (i = 0; i < this.image.width; i++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
this.imageTop = j; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (j = this.image.height - 1; j >= 0; j--) { | ||
n = 0; | ||
this.imageTop = j; | ||
for (i = 0; i < this.image.width; i++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
for (j = this.image.height - 1; j >= 0; j--) { | ||
n = 0; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
for (i = 0; i < this.image.width; i++) { | ||
n = n + this.image.isDark(i, j, 1); | ||
} | ||
this.imageBottom = j; | ||
if (n >= limit) { | ||
break; | ||
} | ||
} | ||
if ((this.imageRight - this.imageLeft + 1 < 21) || (this.imageBottom - this.imageTop + 1 < 21)) { | ||
throw new QRBase.QRError('Found no image data to decode', 3); | ||
} | ||
this.imageBottom = j; | ||
if (Math.abs((this.imageRight - this.imageLeft) - (this.imageBottom - this.imageTop)) > skewLimit) { | ||
throw new QRBase.QRError('Image data is not rectangular', 4); | ||
} | ||
if ((this.imageRight - this.imageLeft + 1 < 21) || (this.imageBottom - this.imageTop + 1 < 21)) { | ||
throw new QRBase.QRError('Found no image data to decode', 3); | ||
} | ||
this.imageSize = ((this.imageRight - this.imageLeft + 1) + (this.imageBottom - this.imageTop + 1)) / 2.0; | ||
}, | ||
findModuleSize: function() { | ||
/** | ||
* returns number of matches found | ||
* perferct is 8*8 = 64 | ||
*/ | ||
function matchFinderPattern(qr, x, y, quietX, quietY, moduleSize) { | ||
var i, j, | ||
n = 0; | ||
if (Math.abs((this.imageRight - this.imageLeft) - (this.imageBottom - this.imageTop)) > skewLimit) { | ||
throw new QRBase.QRError('Image data is not rectangular', 4); | ||
} | ||
// Outer 7x7 black boundary | ||
for (i = 0; i <= 5; i++) { | ||
if (qr.isDarkWithSize(x + i, y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
this.imageSize = ((this.imageRight - this.imageLeft + 1) + (this.imageBottom - this.imageTop + 1)) / 2.0; | ||
}, | ||
findModuleSize: function () { | ||
/** | ||
* returns number of matches found | ||
* perferct is 8*8 = 64 | ||
*/ | ||
function matchFinderPattern(qr, x, y, quietX, quietY, moduleSize) { | ||
var i, j, | ||
n = 0; | ||
if (qr.isDarkWithSize(x + 6, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Outer 7x7 black boundary | ||
for (i = 0; i <= 5; i++) { | ||
if (qr.isDarkWithSize(x + i, y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x + 6 - i, y + 6, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x + 6, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x, y + 6 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (qr.isDarkWithSize(x + 6 - i, y + 6, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Intermediate 5*5 white | ||
for (i = 0; i <= 3; i++) { | ||
if (!qr.isDarkWithSize(x + i + 1, y + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x, y + 6 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + 5, y + i + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Intermediate 5*5 white | ||
for (i = 0; i <= 3; i++) { | ||
if (!qr.isDarkWithSize(x + i + 1, y + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 5 - i, y + 5, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 5, y + i + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 1, y + 5 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + 5 - i, y + 5, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Inner 3*3 black box | ||
for (i = 0; i <= 2; i++) { | ||
for (j = 0; j <= 2; j++) { | ||
if (qr.isDarkWithSize(3 + x, 3 + y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + 1, y + 5 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
// quiet area | ||
for (i = 0; i <= 6; i++) { | ||
if (!qr.isDarkWithSize(x + quietX, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Inner 3*3 black box | ||
for (i = 0; i <= 2; i++) { | ||
for (j = 0; j <= 2; j++) { | ||
if (qr.isDarkWithSize(3 + x, 3 + y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + i, y + quietY, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
// quiet area | ||
for (i = 0; i <= 6; i++) { | ||
if (!qr.isDarkWithSize(x + quietX, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// 'bottom right' quiet area | ||
if (!qr.isDarkWithSize(x + quietX, y + quietY, moduleSize)) { | ||
n = n + 1; | ||
} | ||
return n; | ||
if (!qr.isDarkWithSize(x + i, y + quietY, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
function matchTimingPattern(qr, horizontal, nModules, moduleSize) { | ||
var n = 0, | ||
x0 = 6, | ||
y0 = 8, | ||
dx = 0, | ||
dy = 1, | ||
consecutive = 5, | ||
ok = [], | ||
c, | ||
black = true, | ||
i, | ||
x, y, | ||
last5; | ||
// 'bottom right' quiet area | ||
if (!qr.isDarkWithSize(x + quietX, y + quietY, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (horizontal) { | ||
x0 = 8; | ||
y0 = 6; | ||
dx = 1; | ||
dy = 0; | ||
} | ||
return n; | ||
} | ||
for (c = 0; c < consecutive; c++) { | ||
ok.push(1); | ||
} | ||
function matchTimingPattern(qr, horizontal, nModules, moduleSize) { | ||
var n = 0, | ||
x0 = 6, | ||
y0 = 8, | ||
dx = 0, | ||
dy = 1, | ||
consecutive = 5, | ||
ok = [], | ||
c, | ||
black = true, | ||
i, | ||
x, y, | ||
last5; | ||
for (i = 0; i < nModules - 8 - 8; i++) { | ||
x = x0 + i * dx; | ||
y = y0 + i * dy; | ||
if (horizontal) { | ||
x0 = 8; | ||
y0 = 6; | ||
dx = 1; | ||
dy = 0; | ||
} | ||
if (black === qr.isDarkWithSize(x, y, moduleSize)) { | ||
n++; | ||
ok.push(1); | ||
} else { | ||
ok.push(0); | ||
} | ||
for (c = 0; c < consecutive; c++) { | ||
ok.push(1); | ||
} | ||
black = !black; | ||
last5 = 0; | ||
for (i = 0; i < nModules - 8 - 8; i++) { | ||
x = x0 + i * dx; | ||
y = y0 + i * dy; | ||
for (c = ok.length - consecutive; c < ok.length - 1; c++) { | ||
if (ok[c]) { | ||
last5 = last5 + 1; | ||
} | ||
} | ||
if (black === qr.isDarkWithSize(x, y, moduleSize)) { | ||
n++; | ||
ok.push(1); | ||
} else { | ||
ok.push(0); | ||
} | ||
if (last5 < 3) { | ||
return 0; | ||
} | ||
} | ||
black = !black; | ||
last5 = 0; | ||
return n; | ||
for (c = ok.length - consecutive; c < ok.length - 1; c++) { | ||
if (ok[c]) { | ||
last5 = last5 + 1; | ||
} | ||
} | ||
function matchOneAlignmentPattern(qr, x, y, moduleSize) { | ||
var n = 0, | ||
i; | ||
if (last5 < 3) { | ||
return 0; | ||
} | ||
} | ||
// Outer 5x5 black boundary | ||
for (i = 0; i <= 3; i++) { | ||
if (qr.isDarkWithSize(x + i, y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
return n; | ||
} | ||
if (qr.isDarkWithSize(x + 4, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
function matchOneAlignmentPattern(qr, x, y, moduleSize) { | ||
var n = 0, | ||
i; | ||
if (qr.isDarkWithSize(x + 4 - i, y + 4, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Outer 5x5 black boundary | ||
for (i = 0; i <= 3; i++) { | ||
if (qr.isDarkWithSize(x + i, y, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x, y + 4 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (qr.isDarkWithSize(x + 4, y + i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Intermediate 3*3 white | ||
for (i = 0; i <= 1; i++) { | ||
if (!qr.isDarkWithSize(x + i + 1, y + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x + 4 - i, y + 4, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 3, y + i + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (qr.isDarkWithSize(x, y + 4 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + 3 - i, y + 3, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// Intermediate 3*3 white | ||
for (i = 0; i <= 1; i++) { | ||
if (!qr.isDarkWithSize(x + i + 1, y + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 1, y + 3 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
if (!qr.isDarkWithSize(x + 3, y + i + 1, moduleSize)) { | ||
n = n + 1; | ||
} | ||
// center black | ||
if (qr.isDarkWithSize(x + 2, y + 2, moduleSize)) { | ||
n = n + 1; | ||
} | ||
if (!qr.isDarkWithSize(x + 3 - i, y + 3, moduleSize)) { | ||
n = n + 1; | ||
} | ||
return n; | ||
if (!qr.isDarkWithSize(x + 1, y + 3 - i, moduleSize)) { | ||
n = n + 1; | ||
} | ||
} | ||
function matchAlignmentPatterns(qr, version, moduleSize) { | ||
var a = 0, | ||
n = QRBase.alignmentPatterns[version].length, | ||
i, j, na; | ||
// center black | ||
if (qr.isDarkWithSize(x + 2, y + 2, moduleSize)) { | ||
n = n + 1; | ||
} | ||
for (i = 0; i < n; i++) { | ||
for (j = 0; j < n; j++) { | ||
if (((i === 0) && (j === 0)) || ((i === 0) && (j === n - 1)) || ((i === n - 1) && (j === 0))) { | ||
continue; | ||
} | ||
return n; | ||
} | ||
na = matchOneAlignmentPattern( | ||
qr, | ||
QRBase.alignmentPatterns[version][i] - 2, | ||
QRBase.alignmentPatterns[version][j] - 2, | ||
moduleSize | ||
); | ||
function matchAlignmentPatterns(qr, version, moduleSize) { | ||
var a = 0, | ||
n = QRBase.alignmentPatterns[version].length, | ||
i, j, na; | ||
if (na > 24) { a++; } | ||
} | ||
} | ||
for (i = 0; i < n; i++) { | ||
for (j = 0; j < n; j++) { | ||
if (((i === 0) && (j === 0)) || ((i === 0) && (j === n - 1)) || ((i === n - 1) && (j === 0))) { | ||
continue; | ||
} | ||
return a; | ||
na = matchOneAlignmentPattern( | ||
qr, | ||
QRBase.alignmentPatterns[version][i] - 2, | ||
QRBase.alignmentPatterns[version][j] - 2, | ||
moduleSize | ||
); | ||
if (na > 24) { a++; } | ||
} | ||
} | ||
function matchVersionCode(qr, pattern) { | ||
var v, hd; | ||
return a; | ||
} | ||
for (v = 7; v <= 40; v++) { | ||
hd = qr.hammingDistance(pattern, QRBase.versionInfo[v]); | ||
if (hd <= 3) { return [v, hd]; } | ||
} | ||
function matchVersionCode(qr, pattern) { | ||
var v, hd; | ||
return [0, 4]; | ||
} | ||
for (v = 7; v <= 40; v++) { | ||
hd = qr.hammingDistance(pattern, QRBase.versionInfo[v]); | ||
if (hd <= 3) { return [v, hd]; } | ||
} | ||
function matchVersionTopright(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, y; | ||
return [0, 4]; | ||
} | ||
for (y = 0; y < 6; y++) { | ||
for (x = nModules - 11; x < nModules - 11 + 3; x++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
} | ||
} | ||
function matchVersionTopright(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, y; | ||
return matchVersionCode(qr, pattern); | ||
for (y = 0; y < 6; y++) { | ||
for (x = nModules - 11; x < nModules - 11 + 3; x++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
} | ||
} | ||
function matchVersionBottomleft(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, y; | ||
return matchVersionCode(qr, pattern); | ||
} | ||
for (x = 0; x < 6; x++) { | ||
for (y = nModules - 11; y < nModules - 11 + 3; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
function matchVersionBottomleft(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, y; | ||
factor *= 2; | ||
} | ||
} | ||
for (x = 0; x < 6; x++) { | ||
for (y = nModules - 11; y < nModules - 11 + 3; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
return matchVersionCode(qr, pattern); | ||
factor *= 2; | ||
} | ||
} | ||
function matchFormatCode(qr, pattern) { | ||
var f, hd; | ||
return matchVersionCode(qr, pattern); | ||
} | ||
for (f = 0; f < 32; f++) { | ||
hd = qr.hammingDistance(pattern, QRBase.formatInfo[f]); | ||
function matchFormatCode(qr, pattern) { | ||
var f, hd; | ||
if (hd <= 3) { | ||
return [f, hd]; | ||
} | ||
} | ||
for (f = 0; f < 32; f++) { | ||
hd = qr.hammingDistance(pattern, QRBase.formatInfo[f]); | ||
return [0, 4]; | ||
if (hd <= 3) { | ||
return [f, hd]; | ||
} | ||
} | ||
function matchFormatNW(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x = 8, | ||
y; | ||
return [0, 4]; | ||
} | ||
for (y = 0; y <= 5; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
function matchFormatNW(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x = 8, | ||
y; | ||
factor *= 2; | ||
} | ||
for (y = 0; y <= 5; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
if (qr.isDarkWithSize(8, 7, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
} | ||
factor *= 2; | ||
if (qr.isDarkWithSize(8, 7, moduleSize)) { | ||
pattern += factor; | ||
} | ||
if (qr.isDarkWithSize(8, 8, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
factor *= 2; | ||
if (qr.isDarkWithSize(8, 8, moduleSize)) { | ||
pattern += factor; | ||
} | ||
if (qr.isDarkWithSize(7, 8, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
factor *= 2; | ||
y = 8; | ||
if (qr.isDarkWithSize(7, 8, moduleSize)) { | ||
pattern += factor; | ||
} | ||
for (x = 5; x >= 0; x--) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
y = 8; | ||
factor *= 2; | ||
} | ||
return matchFormatCode(qr, pattern); | ||
for (x = 5; x >= 0; x--) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
function matchFormatNESW(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, | ||
y = 8; | ||
factor *= 2; | ||
} | ||
for (x = nModules - 1; x > nModules - 1 - 8; x--) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
return matchFormatCode(qr, pattern); | ||
} | ||
factor *= 2; | ||
} | ||
function matchFormatNESW(qr, nModules, moduleSize) { | ||
var factor = 1, | ||
pattern = 0, | ||
x, | ||
y = 8; | ||
x = 8; | ||
for (x = nModules - 1; x > nModules - 1 - 8; x--) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
for (y = nModules - 7; y < nModules - 1; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
factor *= 2; | ||
} | ||
factor *= 2; | ||
} | ||
x = 8; | ||
return matchFormatCode(qr, pattern); | ||
for (y = nModules - 7; y < nModules - 1; y++) { | ||
if (qr.isDarkWithSize(x, y, moduleSize)) { | ||
pattern += factor; | ||
} | ||
function gradeFinderPatterns(finderPattern) { | ||
var g = 4, | ||
i; | ||
factor *= 2; | ||
} | ||
for (i = 0; i < 3; i++) { | ||
g = g - (64 - finderPattern[i]); | ||
} | ||
return matchFormatCode(qr, pattern); | ||
} | ||
if (g < 0) { | ||
g = 0; | ||
} | ||
function gradeFinderPatterns(finderPattern) { | ||
var g = 4, | ||
i; | ||
return g; | ||
} | ||
for (i = 0; i < 3; i++) { | ||
g = g - (64 - finderPattern[i]); | ||
} | ||
function gradeTimingPatterns(timingPattern, n) { | ||
var t = (timingPattern[0] + timingPattern[1]) / (2 * n); | ||
if (g < 0) { | ||
g = 0; | ||
} | ||
t = 1 - t; | ||
return g; | ||
} | ||
if (t >= 0.14) { | ||
return 0; | ||
} | ||
function gradeTimingPatterns(timingPattern, n) { | ||
var t = (timingPattern[0] + timingPattern[1]) / (2 * n); | ||
if (t >= 0.11) { | ||
return 1; | ||
} | ||
t = 1 - t; | ||
if (t >= 0.07) { | ||
return 2; | ||
} | ||
if (t >= 0.14) { | ||
return 0; | ||
} | ||
if (t >= 0.00001) { | ||
return 3; | ||
} | ||
if (t >= 0.11) { | ||
return 1; | ||
} | ||
return 4; | ||
} | ||
if (t >= 0.07) { | ||
return 2; | ||
} | ||
function gradeAlignmentPatterns(alignmentPatterns, n) { | ||
var a = alignmentPatterns / n; | ||
if (t >= 0.00001) { | ||
return 3; | ||
} | ||
a = 1 - a; | ||
return 4; | ||
} | ||
if (a >= 0.30) { | ||
return 0; | ||
} | ||
function gradeAlignmentPatterns(alignmentPatterns, n) { | ||
var a = alignmentPatterns / n; | ||
if (a >= 0.20) { | ||
return 1; | ||
} | ||
a = 1 - a; | ||
if (a >= 0.10) { | ||
return 2; | ||
} | ||
if (a >= 0.30) { | ||
return 0; | ||
} | ||
if (a >= 0.00001) { | ||
return 3; | ||
} | ||
if (a >= 0.20) { | ||
return 1; | ||
} | ||
return 4; | ||
} | ||
if (a >= 0.10) { | ||
return 2; | ||
} | ||
function matchVersion(qr, version) { | ||
var g, | ||
grades = [], | ||
nModules = QRBase.nModulesFromVersion(version), | ||
moduleSize = qr.imageSize / nModules, | ||
finderPattern = [0, 0, 0], | ||
versionTopright = [0, 0], | ||
versionBottomleft = [0, 0], | ||
timingPattern = [0, 0], | ||
alignmentPatterns = -3, | ||
format = 0, | ||
v1, formatNW, formatNESW, | ||
ECLevel, mask, grade = 4, | ||
i; | ||
if (a >= 0.00001) { | ||
return 3; | ||
} | ||
finderPattern[0] = matchFinderPattern(qr, 0, 0, 7, 7, moduleSize); | ||
return 4; | ||
} | ||
if (finderPattern[0] < 64 - 3) { | ||
return [version, 0]; // performance hack! | ||
} | ||
function matchVersion(qr, version) { | ||
var g, | ||
grades = [], | ||
nModules = QRBase.nModulesFromVersion(version), | ||
moduleSize = qr.imageSize / nModules, | ||
finderPattern = [0, 0, 0], | ||
versionTopright = [0, 0], | ||
versionBottomleft = [0, 0], | ||
timingPattern = [0, 0], | ||
alignmentPatterns = -3, | ||
format = 0, | ||
v1, formatNW, formatNESW, | ||
ECLevel, mask, grade = 4, | ||
i; | ||
finderPattern[1] = matchFinderPattern(qr, 0, nModules - 7, 7, -1, moduleSize); | ||
finderPattern[0] = matchFinderPattern(qr, 0, 0, 7, 7, moduleSize); | ||
if (finderPattern[0] + finderPattern[1] < 64 + 64 - 3) { | ||
return [version, 0]; // performance hack! | ||
} | ||
if (finderPattern[0] < 64 - 3) { | ||
return [version, 0]; // performance hack! | ||
} | ||
finderPattern[2] = matchFinderPattern(qr, nModules - 7, 0, -1, 7, moduleSize); | ||
g = gradeFinderPatterns(finderPattern); | ||
finderPattern[1] = matchFinderPattern(qr, 0, nModules - 7, 7, -1, moduleSize); | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (finderPattern[0] + finderPattern[1] < 64 + 64 - 3) { | ||
return [version, 0]; // performance hack! | ||
} | ||
if (version >= 7) { | ||
versionTopright = matchVersionTopright(qr, nModules, moduleSize); | ||
versionBottomleft = matchVersionBottomleft(qr, nModules, moduleSize); | ||
v1 = version; | ||
finderPattern[2] = matchFinderPattern(qr, nModules - 7, 0, -1, 7, moduleSize); | ||
g = gradeFinderPatterns(finderPattern); | ||
if (versionTopright[1] < versionBottomleft[1]) { | ||
if (versionTopright[1] < 4) { v1 = versionTopright[0]; } | ||
} else { | ||
if (versionBottomleft[1] < 4) { v1 = versionBottomleft[0]; } | ||
} | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (v1 !== version) { | ||
version = v1; | ||
} | ||
if (version >= 7) { | ||
versionTopright = matchVersionTopright(qr, nModules, moduleSize); | ||
versionBottomleft = matchVersionBottomleft(qr, nModules, moduleSize); | ||
v1 = version; | ||
nModules = QRBase.nModulesFromVersion(version); | ||
moduleSize = qr.imageSize / nModules; | ||
g = Math.round(((4 - versionTopright[1]) + (4 - versionBottomleft[1])) / 2); | ||
if (versionTopright[1] < versionBottomleft[1]) { | ||
if (versionTopright[1] < 4) { v1 = versionTopright[0]; } | ||
} else { | ||
if (versionBottomleft[1] < 4) { v1 = versionBottomleft[0]; } | ||
} | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
} | ||
if (v1 !== version) { | ||
version = v1; | ||
} | ||
timingPattern[0] = matchTimingPattern(qr, true, nModules, moduleSize); | ||
timingPattern[1] = matchTimingPattern(qr, false, nModules, moduleSize); | ||
g = gradeTimingPatterns(timingPattern, nModules - 8 - 8); | ||
nModules = QRBase.nModulesFromVersion(version); | ||
moduleSize = qr.imageSize / nModules; | ||
g = Math.round(((4 - versionTopright[1]) + (4 - versionBottomleft[1])) / 2); | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
} | ||
if (version > 1) { | ||
alignmentPatterns = matchAlignmentPatterns(qr, version, moduleSize); | ||
} | ||
timingPattern[0] = matchTimingPattern(qr, true, nModules, moduleSize); | ||
timingPattern[1] = matchTimingPattern(qr, false, nModules, moduleSize); | ||
g = gradeTimingPatterns(timingPattern, nModules - 8 - 8); | ||
g = gradeAlignmentPatterns(alignmentPatterns, | ||
QRBase.alignmentPatterns[version].length * QRBase.alignmentPatterns[version].length - 3); | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (version > 1) { | ||
alignmentPatterns = matchAlignmentPatterns(qr, version, moduleSize); | ||
} | ||
formatNW = matchFormatNW(qr, nModules, moduleSize); | ||
formatNESW = matchFormatNESW(qr, nModules, moduleSize); | ||
g = gradeAlignmentPatterns(alignmentPatterns, | ||
QRBase.alignmentPatterns[version].length * QRBase.alignmentPatterns[version].length - 3); | ||
if (formatNW[1] < formatNESW[1]) { | ||
format = formatNW[0]; | ||
} else { | ||
format = formatNESW[0]; | ||
} | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
ECLevel = Math.floor(format / 8); | ||
mask = format % 8; | ||
g = Math.round(((4 - formatNW[1]) + (4 - formatNESW[1])) / 2); | ||
formatNW = matchFormatNW(qr, nModules, moduleSize); | ||
formatNESW = matchFormatNESW(qr, nModules, moduleSize); | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
if (formatNW[1] < formatNESW[1]) { | ||
format = formatNW[0]; | ||
} else { | ||
format = formatNESW[0]; | ||
} | ||
for (i = 0; i < grades.length; i++) { | ||
if (grades[i] < grade) { | ||
grade = grades[i]; | ||
} | ||
} | ||
ECLevel = Math.floor(format / 8); | ||
mask = format % 8; | ||
g = Math.round(((4 - formatNW[1]) + (4 - formatNESW[1])) / 2); | ||
return [version, grade, ECLevel, mask]; | ||
if (g < 1) { | ||
return [version, 0]; | ||
} else { | ||
grades.push(g); | ||
} | ||
for (i = 0; i < grades.length; i++) { | ||
if (grades[i] < grade) { | ||
grade = grades[i]; | ||
} | ||
} | ||
/** | ||
* findModuleSize | ||
*/ | ||
var bestMatchSoFar = [0, 0], | ||
version, match; | ||
return [version, grade, ECLevel, mask]; | ||
} | ||
for (version = 1; version <= this.maxVersion; version++) { | ||
match = matchVersion(this, version); | ||
/** | ||
* findModuleSize | ||
*/ | ||
var bestMatchSoFar = [0, 0], | ||
version, match; | ||
if (match[1] > bestMatchSoFar[1]) { bestMatchSoFar = match; } | ||
if (match[1] === 4) { break; } | ||
} | ||
for (version = 1; version <= this.maxVersion; version++) { | ||
match = matchVersion(this, version); | ||
this.version = bestMatchSoFar[0]; | ||
this.nModules = QRBase.nModulesFromVersion(this.version); | ||
this.moduleSize = this.imageSize / this.nModules; | ||
this.functionalGrade = bestMatchSoFar[1]; | ||
this.ECLevel = bestMatchSoFar[2]; | ||
this.mask = bestMatchSoFar[3]; | ||
if (match[1] > bestMatchSoFar[1]) { bestMatchSoFar = match; } | ||
if (match[1] === 4) { break; } | ||
} | ||
if (this.functionalGrade < 1) { | ||
throw new QRBase.QRError('Unable to decode a function pattern', 5); | ||
} | ||
}, | ||
extractCodewords: function() { | ||
function getUnmasked(qr, j, i) { | ||
var m, u; | ||
this.version = bestMatchSoFar[0]; | ||
this.nModules = QRBase.nModulesFromVersion(this.version); | ||
this.moduleSize = this.imageSize / this.nModules; | ||
this.functionalGrade = bestMatchSoFar[1]; | ||
this.ECLevel = bestMatchSoFar[2]; | ||
this.mask = bestMatchSoFar[3]; | ||
switch (qr.mask) { | ||
case 0: | ||
m = (i + j) % 2; | ||
break; | ||
case 1: | ||
m = i % 2; | ||
break; | ||
case 2: | ||
m = j % 3; | ||
break; | ||
case 3: | ||
m = (i + j) % 3; | ||
break; | ||
case 4: | ||
m = (Math.floor(i / 2) + Math.floor(j / 3)) % 2; | ||
break; | ||
case 5: | ||
m = (i * j) % 2 + (i * j) % 3; | ||
break; | ||
case 6: | ||
m = ((i * j) % 2 + (i * j) % 3) % 2; | ||
break; | ||
case 7: | ||
m = ((i + j) % 2 + (i * j) % 3) % 2; | ||
break; | ||
} | ||
if (this.functionalGrade < 1) { | ||
throw new QRBase.QRError('Unable to decode a function pattern', 5); | ||
} | ||
}, | ||
extractCodewords: function () { | ||
function getUnmasked(qr, j, i) { | ||
var m, u; | ||
if (m === 0) { | ||
u = !qr.isDark(j, i); | ||
} else { | ||
u = qr.isDark(j, i); | ||
} | ||
switch (qr.mask) { | ||
case 0: | ||
m = (i + j) % 2; | ||
break; | ||
case 1: | ||
m = i % 2; | ||
break; | ||
case 2: | ||
m = j % 3; | ||
break; | ||
case 3: | ||
m = (i + j) % 3; | ||
break; | ||
case 4: | ||
m = (Math.floor(i / 2) + Math.floor(j / 3)) % 2; | ||
break; | ||
case 5: | ||
m = (i * j) % 2 + (i * j) % 3; | ||
break; | ||
case 6: | ||
m = ((i * j) % 2 + (i * j) % 3) % 2; | ||
break; | ||
case 7: | ||
m = ((i + j) % 2 + (i * j) % 3) % 2; | ||
break; | ||
} | ||
return u; | ||
} | ||
if (m === 0) { | ||
u = !qr.isDark(j, i); | ||
} else { | ||
u = qr.isDark(j, i); | ||
} | ||
/** | ||
* extractCodewords | ||
* Original Java version by Sean Owen | ||
* Copyright 2007 ZXing authors | ||
*/ | ||
this.codewords = []; | ||
return u; | ||
} | ||
var readingUp = true, | ||
currentByte = 0, | ||
factor = 128, | ||
bitsRead = 0, | ||
i, j, col, count; | ||
/** | ||
* extractCodewords | ||
* Original Java version by Sean Owen | ||
* Copyright 2007 ZXing authors | ||
*/ | ||
this.codewords = []; | ||
// Read columns in pairs, from right to left | ||
for (j = this.nModules - 1; j > 0; j -= 2) { | ||
if (j === 6) { | ||
// Skip whole column with vertical alignment pattern; | ||
// saves time and makes the other code proceed more cleanly | ||
j--; | ||
} | ||
var readingUp = true, | ||
currentByte = 0, | ||
factor = 128, | ||
bitsRead = 0, | ||
i, j, col, count; | ||
// Read alternatingly from bottom to top then top to bottom | ||
for (count = 0; count < this.nModules; count++) { | ||
i = readingUp ? this.nModules - 1 - count : count; | ||
// Read columns in pairs, from right to left | ||
for (j = this.nModules - 1; j > 0; j -= 2) { | ||
if (j === 6) { | ||
// Skip whole column with vertical alignment pattern; | ||
// saves time and makes the other code proceed more cleanly | ||
j--; | ||
} | ||
for (col = 0; col < 2; col++) { | ||
// Ignore bits covered by the function pattern | ||
if (!this.functionalPattern[j - col][i]) { | ||
// Read a bit | ||
if (getUnmasked(this, j - col, i)) { | ||
currentByte += factor; | ||
} | ||
// Read alternatingly from bottom to top then top to bottom | ||
for (count = 0; count < this.nModules; count++) { | ||
i = readingUp ? this.nModules - 1 - count : count; | ||
factor /= 2; | ||
// If we've made a whole byte, save it off | ||
if (factor < 1) { | ||
this.codewords.push(currentByte); | ||
bitsRead = 0; | ||
factor = 128; | ||
currentByte = 0; | ||
} | ||
} | ||
} | ||
for (col = 0; col < 2; col++) { | ||
// Ignore bits covered by the function pattern | ||
if (!this.functionalPattern[j - col][i]) { | ||
// Read a bit | ||
if (getUnmasked(this, j - col, i)) { | ||
currentByte += factor; | ||
} | ||
readingUp ^= true; // switch directions | ||
} | ||
}, | ||
extractData: function() { | ||
function extract(qr, bytes, pos, len) { | ||
// http://stackoverflow.com/questions/3846711/extract-bit-sequences-of-arbitrary-length-from-byte-array-efficiently | ||
var shift = 24 - (pos & 7) - len, | ||
mask = (1 << len) - 1, | ||
byteIndex = pos >>> 3; | ||
factor /= 2; | ||
return (((bytes[byteIndex] << 16) | (bytes[++byteIndex] << 8) | bytes[++byteIndex]) >> shift) & mask; | ||
// If we've made a whole byte, save it off | ||
if (factor < 1) { | ||
this.codewords.push(currentByte); | ||
bitsRead = 0; | ||
factor = 128; | ||
currentByte = 0; | ||
} | ||
} | ||
} | ||
} | ||
function extract8bit(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.EightBit, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
i, a; | ||
readingUp ^= true; // switch directions | ||
} | ||
}, | ||
extractData: function () { | ||
function extract(qr, bytes, pos, len) { | ||
// http://stackoverflow.com/questions/3846711/extract-bit-sequences-of-arbitrary-length-from-byte-array-efficiently | ||
var shift = 24 - (pos & 7) - len, | ||
mask = (1 << len) - 1, | ||
byteIndex = pos >>> 3; | ||
qr.bitIdx += nCountBits; | ||
return (((bytes[byteIndex] << 16) | (bytes[++byteIndex] << 8) | bytes[++byteIndex]) >> shift) & mask; | ||
} | ||
for (i = 0; i < n; i++) { | ||
a = extract(qr, bytes, qr.bitIdx, 8); | ||
data += String.fromCharCode(a); | ||
qr.bitIdx += 8; | ||
} | ||
function extract8bit(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.EightBit, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
i, a; | ||
return QRBase.utf8Tounicode(data); | ||
} | ||
qr.bitIdx += nCountBits; | ||
function extractAlphanum(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.AlphaNumeric, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
i, x; | ||
for (i = 0; i < n; i++) { | ||
a = extract(qr, bytes, qr.bitIdx, 8); | ||
data += String.fromCharCode(a); | ||
qr.bitIdx += 8; | ||
} | ||
qr.bitIdx += nCountBits; | ||
return QRBase.utf8Tounicode(data); | ||
} | ||
for (i = 0; i < Math.floor(n / 2); i++) { | ||
x = extract(qr, bytes, qr.bitIdx, 11); | ||
data += qr.alphanum[Math.floor(x / 45)]; | ||
data += qr.alphanum[x % 45]; | ||
qr.bitIdx += 11; | ||
} | ||
function extractAlphanum(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.AlphaNumeric, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
i, x; | ||
if (n % 2) { | ||
data += qr.alphanum[extract(qr, bytes, qr.bitIdx, 6)]; | ||
qr.bitIdx += 6; | ||
} | ||
qr.bitIdx += nCountBits; | ||
return data; | ||
} | ||
for (i = 0; i < Math.floor(n / 2); i++) { | ||
x = extract(qr, bytes, qr.bitIdx, 11); | ||
data += qr.alphanum[Math.floor(x / 45)]; | ||
data += qr.alphanum[x % 45]; | ||
qr.bitIdx += 11; | ||
} | ||
function extractNumeric(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.Numeric, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
x, c1, c2, c3, | ||
i; | ||
if (n % 2) { | ||
data += qr.alphanum[extract(qr, bytes, qr.bitIdx, 6)]; | ||
qr.bitIdx += 6; | ||
} | ||
qr.bitIdx += nCountBits; | ||
return data; | ||
} | ||
for (i = 0; i < Math.floor(n / 3); i++) { | ||
x = extract(qr, bytes, qr.bitIdx, 10); | ||
qr.bitIdx += 10; | ||
c1 = Math.floor(x / 100); | ||
c2 = Math.floor((x % 100) / 10); | ||
c3 = x % 10; | ||
data += String.fromCharCode(48 + c1, 48 + c2, 48 + c3); | ||
} | ||
function extractNumeric(qr, bytes) { | ||
var nCountBits = QRBase.nCountBits(QRBase.MODE.Numeric, qr.version), | ||
n = extract(qr, bytes, qr.bitIdx, nCountBits), | ||
data = '', | ||
x, c1, c2, c3, | ||
i; | ||
if (n % 3 === 1) { | ||
x = extract(qr, bytes, qr.bitIdx, 4); | ||
qr.bitIdx += 4; | ||
data += String.fromCharCode(48 + x); | ||
} else if (n % 3 === 2) { | ||
x = extract(qr, bytes, qr.bitIdx, 7); | ||
qr.bitIdx += 7; | ||
c1 = Math.floor(x / 10); | ||
c2 = x % 10; | ||
data += String.fromCharCode(48 + c1, 48 + c2); | ||
} | ||
qr.bitIdx += nCountBits; | ||
return data; | ||
} | ||
for (i = 0; i < Math.floor(n / 3); i++) { | ||
x = extract(qr, bytes, qr.bitIdx, 10); | ||
qr.bitIdx += 10; | ||
c1 = Math.floor(x / 100); | ||
c2 = Math.floor((x % 100) / 10); | ||
c3 = x % 10; | ||
data += String.fromCharCode(48 + c1, 48 + c2, 48 + c3); | ||
} | ||
// extractData | ||
var bytes = this.bytes, | ||
nBits = bytes.length * 8, | ||
i, mode; | ||
if (n % 3 === 1) { | ||
x = extract(qr, bytes, qr.bitIdx, 4); | ||
qr.bitIdx += 4; | ||
data += String.fromCharCode(48 + x); | ||
} else if (n % 3 === 2) { | ||
x = extract(qr, bytes, qr.bitIdx, 7); | ||
qr.bitIdx += 7; | ||
c1 = Math.floor(x / 10); | ||
c2 = x % 10; | ||
data += String.fromCharCode(48 + c1, 48 + c2); | ||
} | ||
for (i = 0; i < 4; i++) { | ||
bytes.push(0); | ||
} | ||
return data; | ||
} | ||
this.data = ''; | ||
this.bitIdx = 0; | ||
// extractData | ||
var bytes = this.bytes, | ||
nBits = bytes.length * 8, | ||
i, mode; | ||
while (this.bitIdx < nBits - 4) { | ||
mode = extract(this, bytes, this.bitIdx, 4); | ||
this.bitIdx += 4; | ||
for (i = 0; i < 4; i++) { | ||
bytes.push(0); | ||
} | ||
if (mode === QRBase.MODE.Terminator) { break; } else if (mode === QRBase.MODE.AlphaNumeric) { this.data += extractAlphanum(this, bytes); } else if (mode === QRBase.MODE.EightBit) { this.data += extract8bit(this, bytes); } else if (mode === QRBase.MODE.Numeric) { this.data += extractNumeric(this, bytes); } else { throw new QRBase.QRError('Unsupported ECI mode: ' + mode, 1, mode); } | ||
} | ||
}, | ||
correctErrors: function() { | ||
var rs = new ReedSolomon(this.nBlockEcWords), | ||
errors = [], | ||
bytes = [], | ||
b, | ||
bytesIn, bytesOut, | ||
i; | ||
this.data = ''; | ||
this.bitIdx = 0; | ||
for (b = 0; b < this.blockIndices.length; b++) { | ||
bytesIn = []; | ||
while (this.bitIdx < nBits - 4) { | ||
mode = extract(this, bytes, this.bitIdx, 4); | ||
this.bitIdx += 4; | ||
for (i = 0; i < this.blockIndices[b].length; i++) { | ||
bytesIn.push(this.codewords[this.blockIndices[b][i]]); | ||
} | ||
if (mode === QRBase.MODE.Terminator) { break; } else if (mode === QRBase.MODE.AlphaNumeric) { this.data += extractAlphanum(this, bytes); } else if (mode === QRBase.MODE.EightBit) { this.data += extract8bit(this, bytes); } else if (mode === QRBase.MODE.Numeric) { this.data += extractNumeric(this, bytes); } else { throw new QRBase.QRError('Unsupported ECI mode: ' + mode, 1, mode); } | ||
} | ||
}, | ||
correctErrors: function () { | ||
var rs = new ReedSolomon(this.nBlockEcWords), | ||
errors = [], | ||
bytes = [], | ||
b, | ||
bytesIn, bytesOut, | ||
i; | ||
bytesOut = rs.decode(bytesIn); | ||
for (b = 0; b < this.blockIndices.length; b++) { | ||
bytesIn = []; | ||
if (!rs.corrected) { | ||
this.errorGrade = 0; | ||
throw new QRBase.QRError('Unable to correct errors (' + rs.uncorrected_reason + ')', 6, rs.uncorrected_reason); | ||
} | ||
for (i = 0; i < this.blockIndices[b].length; i++) { | ||
bytesIn.push(this.codewords[this.blockIndices[b][i]]); | ||
} | ||
bytes = bytes.concat(bytesOut); | ||
errors.push(rs.n_errors); | ||
} | ||
bytesOut = rs.decode(bytesIn); | ||
this.errors = errors; | ||
this.bytes = bytes; | ||
this.errorGrade = this.gradeErrors(errors); | ||
}, | ||
gradeErrors: function(errors) { | ||
var ecw = this.nBlockEcWords, | ||
max = 0, | ||
grade = 4, | ||
i; | ||
if (!rs.corrected) { | ||
this.errorGrade = 0; | ||
throw new QRBase.QRError('Unable to correct errors (' + rs.uncorrected_reason + ')', 6, rs.uncorrected_reason); | ||
} | ||
for (i = 0; i < errors.length; i++) { | ||
if (errors[i] > max) { max = errors[i]; } | ||
} | ||
bytes = bytes.concat(bytesOut); | ||
errors.push(rs.n_errors); | ||
} | ||
if (max > ecw / 2 - 1) { | ||
grade = 0; | ||
} else if (max > ecw / 2 - 2) { | ||
grade = 1; | ||
} else if (max > ecw / 2 - 3) { | ||
grade = 2; | ||
} else if (max > ecw / 2 - 4) { | ||
grade = 3; | ||
} | ||
this.errors = errors; | ||
this.bytes = bytes; | ||
this.errorGrade = this.gradeErrors(errors); | ||
}, | ||
gradeErrors: function (errors) { | ||
var ecw = this.nBlockEcWords, | ||
max = 0, | ||
grade = 4, | ||
i; | ||
return grade; | ||
}, | ||
hammingDistance: function(a, b) { | ||
function nBits(n) { | ||
var c; | ||
for (i = 0; i < errors.length; i++) { | ||
if (errors[i] > max) { max = errors[i]; } | ||
} | ||
for (c = 0; n; c++) { | ||
n &= n - 1; // clear the least significant bit set | ||
} | ||
if (max > ecw / 2 - 1) { | ||
grade = 0; | ||
} else if (max > ecw / 2 - 2) { | ||
grade = 1; | ||
} else if (max > ecw / 2 - 3) { | ||
grade = 2; | ||
} else if (max > ecw / 2 - 4) { | ||
grade = 3; | ||
} | ||
return c; | ||
} | ||
return grade; | ||
}, | ||
hammingDistance: function (a, b) { | ||
function nBits(n) { | ||
var c; | ||
var d = a ^ b; | ||
for (c = 0; n; c++) { | ||
n &= n - 1; // clear the least significant bit set | ||
} | ||
return nBits(d); | ||
}, | ||
/** | ||
* QRCodeDecode IMAGE FUNCTIONS | ||
*/ | ||
isDarkWithSize: function(x, y, moduleSize) { | ||
return this.image.isDark(Math.round(this.imageLeft + x * moduleSize), | ||
Math.round(this.imageTop + y * moduleSize), Math.round(moduleSize)); | ||
}, | ||
isDark: function(x, y) { | ||
return this.isDarkWithSize(x, y, this.moduleSize); | ||
}, | ||
/** | ||
* QRCode decode constants | ||
*/ | ||
alphanum: [ | ||
'0', | ||
'1', | ||
'2', | ||
'3', | ||
'4', | ||
'5', | ||
'6', | ||
'7', | ||
'8', | ||
'9', | ||
'A', | ||
'B', | ||
'C', | ||
'D', | ||
'E', | ||
'F', | ||
'G', | ||
'H', | ||
'I', | ||
'J', | ||
'K', | ||
'L', | ||
'M', | ||
'N', | ||
'O', | ||
'P', | ||
'Q', | ||
'R', | ||
'S', | ||
'T', | ||
'U', | ||
'V', | ||
'W', | ||
'X', | ||
'Y', | ||
'Z', | ||
' ', | ||
'$', | ||
'%', | ||
'*', | ||
'+', | ||
'-', | ||
'.', | ||
'/', | ||
':' | ||
] | ||
return c; | ||
} | ||
var d = a ^ b; | ||
return nBits(d); | ||
}, | ||
/** | ||
* QRCodeDecode IMAGE FUNCTIONS | ||
*/ | ||
isDarkWithSize: function (x, y, moduleSize) { | ||
return this.image.isDark(Math.round(this.imageLeft + x * moduleSize), | ||
Math.round(this.imageTop + y * moduleSize), Math.round(moduleSize)); | ||
}, | ||
isDark: function (x, y) { | ||
return this.isDarkWithSize(x, y, this.moduleSize); | ||
}, | ||
/** | ||
* QRCode decode constants | ||
*/ | ||
alphanum: [ | ||
'0', | ||
'1', | ||
'2', | ||
'3', | ||
'4', | ||
'5', | ||
'6', | ||
'7', | ||
'8', | ||
'9', | ||
'A', | ||
'B', | ||
'C', | ||
'D', | ||
'E', | ||
'F', | ||
'G', | ||
'H', | ||
'I', | ||
'J', | ||
'K', | ||
'L', | ||
'M', | ||
'N', | ||
'O', | ||
'P', | ||
'Q', | ||
'R', | ||
'S', | ||
'T', | ||
'U', | ||
'V', | ||
'W', | ||
'X', | ||
'Y', | ||
'Z', | ||
' ', | ||
'$', | ||
'%', | ||
'*', | ||
'+', | ||
'-', | ||
'.', | ||
'/', | ||
':' | ||
] | ||
}; | ||
export default QRDecode | ||
export default QRDecode |
/** | ||
* QRDecode | ||
* QREncode | ||
*/ | ||
'use strict'; | ||
import QRDecode from './decode' | ||
import QREncode from './encode' | ||
let QREncodeConf = { | ||
/** | ||
* 配置 | ||
*/ | ||
config: { | ||
text: 'QRCode', // 默认文字 | ||
render: 'canvas', | ||
bgColor: '#FFF', // 背景色 | ||
moduleColor: '#000', // 前景色 | ||
moduleSize: 5, // 模块大小 | ||
mode: 4, // 编码格式,默认8字节编码 | ||
ECLevel: 2, // 纠错码等级,默认30% | ||
margin: 4, // 留白 | ||
logo: '', // logo; | ||
error: () => { } | ||
}, | ||
Render: { | ||
canvas: function (self, callback) { | ||
var i; | ||
var j; | ||
var cfg = self.config; | ||
var mSize = cfg.moduleSize; | ||
var size = self.pixArr.length; | ||
var outSize = 2 * cfg.margin + size; | ||
var canvas = document.createElement('canvas'); | ||
var ctx = canvas.getContext('2d'); | ||
function getRGB(color) { | ||
var red, green, blue; | ||
if (color.indexOf('#') === 0) { | ||
color = color.substr(1); | ||
} | ||
if (color.length === 6) { | ||
red = color.substr(0, 2); | ||
green = color.substr(2, 2); | ||
blue = color.substr(4, 2); | ||
} else if (color.length === 3) { | ||
red = color.substr(0, 1); | ||
red += red; | ||
green = color.substr(1, 1); | ||
green += green; | ||
blue = color.substr(2, 1); | ||
blue += blue; | ||
} else { | ||
throw new Error('Error color'); | ||
} | ||
return 'rgb(' + parseInt(red, 16) + ', ' + parseInt(green, 16) + ', ' + parseInt(blue, 16) + ')'; | ||
} | ||
// 初始化画布 | ||
function init(size) { | ||
size = cfg.margin * 2 + size; | ||
canvas.width = size * mSize; | ||
canvas.height = size * mSize; | ||
ctx.fillStyle = getRGB(cfg.bgColor); | ||
ctx.fillRect(0, 0, canvas.width, canvas.height); | ||
} | ||
// 设置色块 | ||
function setBlock(i, j) { | ||
ctx.fillStyle = getRGB(cfg.moduleColor); | ||
ctx.fillRect(i * mSize, j * mSize, mSize, mSize); | ||
} | ||
// 渲染logo | ||
function renderLogo(callback) { | ||
var img = new Image(); | ||
img.onload = function () { | ||
var x; | ||
var y; | ||
var zoom; | ||
var imgW = img.width; | ||
var imgH = img.height; | ||
var imgSize = Math.max(imgW, imgH); | ||
if (imgSize > size * mSize * 0.3) { | ||
zoom = (size * mSize * 0.3) / imgSize; | ||
imgW = imgW * zoom; | ||
imgH = imgH * zoom; | ||
} | ||
x = Math.round((outSize * mSize - imgW) / 2); | ||
y = Math.round((outSize * mSize - imgH) / 2); | ||
ctx.drawImage(img, x, y, imgW, imgH); | ||
typeof callback === 'function' && callback(); | ||
img.onload = null; | ||
}; | ||
img.src = cfg.logo; | ||
} | ||
init(size); | ||
for (i = cfg.margin; i < size + cfg.margin; i++) { | ||
for (j = cfg.margin; j < size + cfg.margin; j++) { | ||
if (self.pixArr[i - cfg.margin][j - cfg.margin]) { | ||
setBlock(i, j, getRGB(cfg.moduleColor)); | ||
} | ||
} | ||
} | ||
if (cfg.logo) { | ||
renderLogo(function () { | ||
typeof callback === 'function' && callback(canvas); | ||
}); | ||
} else { | ||
typeof callback === 'function' && callback(canvas); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* 二维码解码 | ||
* @param canvas | ||
* @param error | ||
* 二维码编码 | ||
* @param config | ||
* @param callback | ||
* @returns {*} | ||
* @constructor | ||
*/ | ||
function Decode(canvas, error) { | ||
this.text = this._init(this.getImageData(canvas), error); | ||
function Encode(config, callback) { | ||
this.config = config; | ||
// 初始化 | ||
return this._init(callback); | ||
} | ||
Decode.prototype = { | ||
_init: function (imageData, error) { | ||
var text = ''; | ||
var qr = new QRDecode(); | ||
Encode.prototype = { | ||
_init: function (callback) { | ||
var config = this.config; | ||
this.qr = new QREncode(); | ||
// 含有 Logo,使用最大容错 | ||
if (config.logo) { | ||
config.ECLevel = 2; | ||
} | ||
try { | ||
text = qr.decodeImageData(imageData, this.width, this.height); | ||
this.version = this.qr.getVersionFromLength(config.ECLevel, config.mode, config.text); | ||
this.pixArr = this.qr.encodeToPix(config.mode, config.text, this.version, config.ECLevel); | ||
} catch (e) { | ||
typeof error === 'function' && error(e); | ||
typeof error === 'function' && config.error(e); | ||
} | ||
return text; | ||
}, | ||
getImageData: function (canvas) { | ||
var ctx; | ||
if (canvas.nodeName.toLowerCase() !== 'canvas') { | ||
canvas = canvas | ||
if (!canvas) return null; | ||
} | ||
ctx = canvas.getContext('2d'); | ||
this.width = canvas.width; | ||
this.height = canvas.height; | ||
return ctx.getImageData(0, 0, canvas.width, canvas.height); | ||
this.pixArr && QREncodeConf.Render[config.render](this, callback); | ||
} | ||
@@ -46,15 +164,27 @@ }; | ||
/** | ||
* 二维码解码 | ||
* @param error | ||
* @returns {string} | ||
* 二维码编码 | ||
* @param cfg | ||
* @constructor | ||
*/ | ||
export default function (canvas) { | ||
var text = ''; | ||
var error = function (e) { | ||
console.log('error', e) | ||
export default function (cfg) { | ||
var that = this; | ||
var config = {}; | ||
if (typeof (cfg) === 'string') { | ||
config.text = cfg; | ||
} else { | ||
config = Object.assign({}, QREncodeConf.config, cfg) | ||
} | ||
text = (new Decode(canvas, error)).text; | ||
return text; | ||
config.moduleSize = Math.round(config.moduleSize); | ||
config.margin = Math.round(config.margin); | ||
config.moduleSize = config.moduleSize > 0 ? config.moduleSize : QREncodeConf.config.moduleSize; | ||
config.margin = config.margin < 0 ? QREncodeConf.config.margin : config.margin; | ||
let result | ||
let encodeobj = new Encode(config, function (qrdom) { | ||
result = qrdom | ||
}) | ||
return result | ||
}; |
{ | ||
"name": "qrcode-pure", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "qrcode encode and decode", | ||
@@ -15,4 +15,4 @@ "main": "index.js", | ||
"webpack": "^4.10.2", | ||
"webpack-cli": "^3.0.0" | ||
"webpack-cli": "^3.0.1" | ||
} | ||
} |
@@ -1,2 +0,86 @@ | ||
# qrcode-tool | ||
# qrcode-pure | ||
### 简介 | ||
qrcode encode and decode in js (without jquery) | ||
生成\解码二维码的函数。核心源码来自项目[qrcode](https://github.com/nuintun/qrcode),但qrcode使用的是jquery封装,qrcode-pure在此将两个功能拆分了出来。 | ||
### 安装 | ||
``` bash | ||
# 安装依赖 | ||
npm i install --save qrcode-pure | ||
``` | ||
### 使用 | ||
#### 解码 | ||
在项目中使用:(vue单文件组件为例) | ||
``` html | ||
<!-- template --> | ||
<input type="file" @change="handleChange"> | ||
<canvas ref="decode-canvas"> | ||
<button @click="decode">点击解码</button> | ||
``` | ||
``` javascript | ||
// 引入解码函数 | ||
// script | ||
import qedecode from 'qrcode-pure/lib/qrdecode' | ||
// methods | ||
handleChange(e) { | ||
var canvas = this.$refs['decode-canvas'], | ||
ctx = canvas.getContext('2d'), | ||
file = e.target.files[0], | ||
reader = new FileReader() | ||
reader.onload = function(e) { | ||
var img = new Image() | ||
img.onload = function() { | ||
canvas.width = img.width | ||
canvas.height = img.height | ||
ctx.drawImage(img, 0, 0) | ||
} | ||
img.src = e.target.result | ||
} | ||
file && reader.readAsDataURL(file) | ||
} | ||
// 调用解码函数 | ||
// 传入一个canvas,内容为二维码 | ||
// 如果是上传的图片,需要调用canvas的drawImage方法,生成canvas | ||
function decode(){ | ||
let result = qedecode(this.$refs['decode-canvas') | ||
// result返回值为解码后的值 | ||
console.log('result', result) | ||
} | ||
``` | ||
#### 编码 | ||
在项目中使用:(vue单文件组件为例) | ||
``` html | ||
<!-- template --> | ||
<el-button @click="encode" type="primary" size="mini" class="m-l_2">编码</el-button> | ||
``` | ||
``` javascript | ||
// 引入解码函数 | ||
// script | ||
import qrencode from 'qrcode-pure/lib/qrencode' | ||
// methods | ||
encode() { | ||
// 调用 qrencode 编码函数,可接受一个对象参数 | ||
// 参数具体内容请参考项目[qrcode](https://github.com/nuintun/qrcode),在此不再赘述。 | ||
// 返回值为二维码canvas的DOM | ||
let result = qrencode({ text: 'https://pkjy.github.io' }) | ||
} | ||
``` | ||
### 在线体验 | ||
[qrcode-pure尝试](https://pkjy.github.io/#/gallery/qrcode-pure) |
80034
11
2763
87