node-ejs-renderer/node_modules/qrcode/lib/core/mask-pattern.js

235 lines
6.0 KiB
JavaScript
Raw Permalink Normal View History

2024-06-09 13:55:01 -04:00
/**
* Data mask pattern reference
* @type {Object}
*/
exports.Patterns = {
PATTERN000: 0,
PATTERN001: 1,
PATTERN010: 2,
PATTERN011: 3,
PATTERN100: 4,
PATTERN101: 5,
PATTERN110: 6,
PATTERN111: 7
}
/**
* Weighted penalty scores for the undesirable features
* @type {Object}
*/
const PenaltyScores = {
N1: 3,
N2: 3,
N3: 40,
N4: 10
}
/**
* Check if mask pattern value is valid
*
* @param {Number} mask Mask pattern
* @return {Boolean} true if valid, false otherwise
*/
exports.isValid = function isValid (mask) {
return mask != null && mask !== '' && !isNaN(mask) && mask >= 0 && mask <= 7
}
/**
* Returns mask pattern from a value.
* If value is not valid, returns undefined
*
* @param {Number|String} value Mask pattern value
* @return {Number} Valid mask pattern or undefined
*/
exports.from = function from (value) {
return exports.isValid(value) ? parseInt(value, 10) : undefined
}
/**
* Find adjacent modules in row/column with the same color
* and assign a penalty value.
*
* Points: N1 + i
* i is the amount by which the number of adjacent modules of the same color exceeds 5
*/
exports.getPenaltyN1 = function getPenaltyN1 (data) {
const size = data.size
let points = 0
let sameCountCol = 0
let sameCountRow = 0
let lastCol = null
let lastRow = null
for (let row = 0; row < size; row++) {
sameCountCol = sameCountRow = 0
lastCol = lastRow = null
for (let col = 0; col < size; col++) {
let module = data.get(row, col)
if (module === lastCol) {
sameCountCol++
} else {
if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
lastCol = module
sameCountCol = 1
}
module = data.get(col, row)
if (module === lastRow) {
sameCountRow++
} else {
if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
lastRow = module
sameCountRow = 1
}
}
if (sameCountCol >= 5) points += PenaltyScores.N1 + (sameCountCol - 5)
if (sameCountRow >= 5) points += PenaltyScores.N1 + (sameCountRow - 5)
}
return points
}
/**
* Find 2x2 blocks with the same color and assign a penalty value
*
* Points: N2 * (m - 1) * (n - 1)
*/
exports.getPenaltyN2 = function getPenaltyN2 (data) {
const size = data.size
let points = 0
for (let row = 0; row < size - 1; row++) {
for (let col = 0; col < size - 1; col++) {
const last = data.get(row, col) +
data.get(row, col + 1) +
data.get(row + 1, col) +
data.get(row + 1, col + 1)
if (last === 4 || last === 0) points++
}
}
return points * PenaltyScores.N2
}
/**
* Find 1:1:3:1:1 ratio (dark:light:dark:light:dark) pattern in row/column,
* preceded or followed by light area 4 modules wide
*
* Points: N3 * number of pattern found
*/
exports.getPenaltyN3 = function getPenaltyN3 (data) {
const size = data.size
let points = 0
let bitsCol = 0
let bitsRow = 0
for (let row = 0; row < size; row++) {
bitsCol = bitsRow = 0
for (let col = 0; col < size; col++) {
bitsCol = ((bitsCol << 1) & 0x7FF) | data.get(row, col)
if (col >= 10 && (bitsCol === 0x5D0 || bitsCol === 0x05D)) points++
bitsRow = ((bitsRow << 1) & 0x7FF) | data.get(col, row)
if (col >= 10 && (bitsRow === 0x5D0 || bitsRow === 0x05D)) points++
}
}
return points * PenaltyScores.N3
}
/**
* Calculate proportion of dark modules in entire symbol
*
* Points: N4 * k
*
* k is the rating of the deviation of the proportion of dark modules
* in the symbol from 50% in steps of 5%
*/
exports.getPenaltyN4 = function getPenaltyN4 (data) {
let darkCount = 0
const modulesCount = data.data.length
for (let i = 0; i < modulesCount; i++) darkCount += data.data[i]
const k = Math.abs(Math.ceil((darkCount * 100 / modulesCount) / 5) - 10)
return k * PenaltyScores.N4
}
/**
* Return mask value at given position
*
* @param {Number} maskPattern Pattern reference value
* @param {Number} i Row
* @param {Number} j Column
* @return {Boolean} Mask value
*/
function getMaskAt (maskPattern, i, j) {
switch (maskPattern) {
case exports.Patterns.PATTERN000: return (i + j) % 2 === 0
case exports.Patterns.PATTERN001: return i % 2 === 0
case exports.Patterns.PATTERN010: return j % 3 === 0
case exports.Patterns.PATTERN011: return (i + j) % 3 === 0
case exports.Patterns.PATTERN100: return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0
case exports.Patterns.PATTERN101: return (i * j) % 2 + (i * j) % 3 === 0
case exports.Patterns.PATTERN110: return ((i * j) % 2 + (i * j) % 3) % 2 === 0
case exports.Patterns.PATTERN111: return ((i * j) % 3 + (i + j) % 2) % 2 === 0
default: throw new Error('bad maskPattern:' + maskPattern)
}
}
/**
* Apply a mask pattern to a BitMatrix
*
* @param {Number} pattern Pattern reference number
* @param {BitMatrix} data BitMatrix data
*/
exports.applyMask = function applyMask (pattern, data) {
const size = data.size
for (let col = 0; col < size; col++) {
for (let row = 0; row < size; row++) {
if (data.isReserved(row, col)) continue
data.xor(row, col, getMaskAt(pattern, row, col))
}
}
}
/**
* Returns the best mask pattern for data
*
* @param {BitMatrix} data
* @return {Number} Mask pattern reference number
*/
exports.getBestMask = function getBestMask (data, setupFormatFunc) {
const numPatterns = Object.keys(exports.Patterns).length
let bestPattern = 0
let lowerPenalty = Infinity
for (let p = 0; p < numPatterns; p++) {
setupFormatFunc(p)
exports.applyMask(p, data)
// Calculate penalty
const penalty =
exports.getPenaltyN1(data) +
exports.getPenaltyN2(data) +
exports.getPenaltyN3(data) +
exports.getPenaltyN4(data)
// Undo previously applied mask
exports.applyMask(p, data)
if (penalty < lowerPenalty) {
lowerPenalty = penalty
bestPattern = p
}
}
return bestPattern
}