UNPKG

@nuintun/qrcode

Version:

A pure JavaScript QRCode encode and decode library.

198 lines (193 loc) 7.38 kB
/** * @module QRCode * @package @nuintun/qrcode * @license MIT * @version 5.0.2 * @author nuintun <nuintun@qq.com> * @description A pure JavaScript QRCode encode and decode library. * @see https://github.com/nuintun/qrcode#readme */ 'use strict'; const Pattern = require('./Pattern.cjs'); const utils = require('../common/utils.cjs'); const Point = require('../common/Point.cjs'); const scanline = require('./utils/scanline.cjs'); const PatternRatios = require('./PatternRatios.cjs'); const timing = require('./utils/timing.cjs'); const PatternFinder = require('./PatternFinder.cjs'); const pattern = require('./utils/pattern.cjs'); const Version = require('../common/Version.cjs'); const FinderPatternGroup = require('./FinderPatternGroup.cjs'); const constants = require('./utils/constants.cjs'); /** * @module FinderPatternFinder */ function isGroupNested(finderPatternGroup, patterns, used) { let count = 0; const { topLeft, topRight, bottomLeft } = finderPatternGroup; for (const pattern of patterns) { if (pattern !== topLeft && pattern !== topRight && pattern !== bottomLeft) { let contain; if (used.has(pattern)) { contain = FinderPatternGroup.FinderPatternGroup.contains(finderPatternGroup, pattern); if (contain) { return true; } } if ( Pattern.Pattern.noise(pattern) < 1 && (contain == null ? FinderPatternGroup.FinderPatternGroup.contains(finderPatternGroup, pattern) : contain) ) { // Maybe contain another QR code, but we only allow one, because this is not a normal mode. if (++count > 3) { return true; } } } } return false; } class FinderPatternFinder extends PatternFinder.PatternFinder { constructor(matrix, strict) { super(matrix, PatternRatios.FINDER_PATTERN_RATIOS, strict); } *groups() { const patterns = this.patterns.filter(pattern => { return Pattern.Pattern.combined(pattern) >= 3 && Pattern.Pattern.noise(pattern) <= 1.5; }); const { length } = patterns; if (length === 3) { const finderPatternGroup = new FinderPatternGroup.FinderPatternGroup(this.matrix, patterns); const size = FinderPatternGroup.FinderPatternGroup.size(finderPatternGroup); if (size >= Version.MIN_VERSION_SIZE && size <= Version.MAX_VERSION_SIZE) { yield finderPatternGroup; } } else if (length > 3) { const maxI1 = length - 2; const maxI2 = length - 1; const used = new Map(); for (let i1 = 0; i1 < maxI1; i1++) { const pattern1 = patterns[i1]; const moduleSize1 = pattern1.moduleSize; // Pattern 1 used. if (used.has(pattern1)) { continue; } for (let i2 = i1 + 1; i2 < maxI2; i2++) { const pattern2 = patterns[i2]; const moduleSize2 = pattern2.moduleSize; // Pattern 1 used. if (used.has(pattern1)) { break; } if ( // Pattern 2 used. used.has(pattern2) || // Non equals module size. !pattern.isEqualsSize(moduleSize1, moduleSize2, constants.DIFF_MODULE_SIZE_RATIO) ) { continue; } for (let i3 = i2 + 1; i3 < length; i3++) { const pattern3 = patterns[i3]; const moduleSize3 = pattern3.moduleSize; if ( // Pattern 1 used. used.has(pattern1) || // Pattern 2 used. used.has(pattern2) ) { break; } if ( // Non equals module size. !pattern.isEqualsSize(moduleSize1, moduleSize3, constants.DIFF_MODULE_SIZE_RATIO) || // Non equals module size. !pattern.isEqualsSize(moduleSize2, moduleSize3, constants.DIFF_MODULE_SIZE_RATIO) ) { continue; } const { matrix } = this; const finderPatternGroup = new FinderPatternGroup.FinderPatternGroup(matrix, [pattern1, pattern2, pattern3]); const angle = FinderPatternGroup.calculateTopLeftAngle(finderPatternGroup); if (angle >= constants.MIN_TOP_LEFT_ANGLE && angle <= constants.MAX_TOP_LEFT_ANGLE) { const [xModuleSize, yModuleSize] = FinderPatternGroup.FinderPatternGroup.moduleSizes(finderPatternGroup); if (xModuleSize >= 1 && yModuleSize >= 1) { const { topLeft, topRight, bottomLeft } = finderPatternGroup; const edge1 = Point.distance(topLeft, topRight); const edge2 = Point.distance(topLeft, bottomLeft); const edge1Modules = utils.round(edge1 / xModuleSize); const edge2Modules = utils.round(edge2 / yModuleSize); if (Math.abs(edge1Modules - edge2Modules) <= 4) { const size = FinderPatternGroup.FinderPatternGroup.size(finderPatternGroup); if ( size >= Version.MIN_VERSION_SIZE && size <= Version.MAX_VERSION_SIZE && !isGroupNested(finderPatternGroup, patterns, used) ) { if ( timing.checkEstimateTimingLine(matrix, finderPatternGroup) || timing.checkEstimateTimingLine(matrix, finderPatternGroup, true) ) { if (yield finderPatternGroup) { used.set(pattern1, true); used.set(pattern2, true); used.set(pattern3, true); } } } } } } } } } } } find(left, top, width, height) { const { matrix } = this; const right = left + width; const bottom = top + height; const match = (x, y, scanline$1, count, scanlineBits, lastBit) => { scanline.scanlineUpdate(scanline$1, count); scanline.scanlineUpdate(scanlineBits, lastBit); // Match pattern black-white-black-white-black. if ( scanlineBits[0] === 1 && scanlineBits[1] === 0 && scanlineBits[2] === 1 && scanlineBits[3] === 0 && scanlineBits[4] === 1 && pattern.isMatchPattern(scanline$1, PatternRatios.FINDER_PATTERN_RATIOS) ) { this.match(x, y, scanline$1, scanline$1[2]); } }; for (let y = top; y < bottom; y++) { let x = left; // Burn off leading white pixels before anything else; if we start in the middle of // a white run, it doesn't make sense to count its length, since we don't know if the // white run continued to the left of the start point. while (x < right && !matrix.get(x, y)) { x++; } let count = 0; let lastBit = matrix.get(x, y); const scanline = [0, 0, 0, 0, 0]; const scanlineBits = [-1, -1, -1, -1, -1]; while (x < right) { const bit = matrix.get(x, y); if (bit === lastBit) { count++; } else { match(x, y, scanline, count, scanlineBits, lastBit); count = 1; lastBit = bit; } x++; } match(x, y, scanline, count, scanlineBits, lastBit); } } } exports.FinderPatternFinder = FinderPatternFinder;