Completely rewrote the algorithm for detecting finder-like patterns, making it more accurate and compliant with the QR Code specification, in all languages.

pull/40/head
Project Nayuki 6 years ago
parent 5ac0e2a938
commit af872343c0

@ -72,6 +72,8 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco
static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]);
static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask);
static long getPenaltyScore(const uint8_t qrcode[]);
static void addRunToHistory(unsigned char run, unsigned char history[7]);
static bool hasFinderLikePattern(unsigned char runHistory[7]);
testable bool getModule(const uint8_t qrcode[], int x, int y);
testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack);
@ -630,10 +632,11 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
int qrsize = qrcodegen_getSize(qrcode);
long result = 0;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for (int y = 0; y < qrsize; y++) {
unsigned char runHistory[7] = {0};
bool color = false;
int runX = 0;
unsigned char runX = 0;
for (int x = 0; x < qrsize; x++) {
if (getModule(qrcode, x, y) == color) {
runX++;
@ -642,15 +645,24 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = getModule(qrcode, x, y);
runX = 1;
}
}
addRunToHistory(runX, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for (int x = 0; x < qrsize; x++) {
unsigned char runHistory[7] = {0};
bool color = false;
int runY = 0;
unsigned char runY = 0;
for (int y = 0; y < qrsize; y++) {
if (getModule(qrcode, x, y) == color) {
runY++;
@ -659,10 +671,18 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = getModule(qrcode, x, y);
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -676,23 +696,6 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
}
}
// Finder-like pattern in rows
for (int y = 0; y < qrsize; y++) {
for (int x = 0, bits = 0; x < qrsize; x++) {
bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0);
if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Finder-like pattern in columns
for (int x = 0; x < qrsize; x++) {
for (int y = 0, bits = 0; y < qrsize; y++) {
bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0);
if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Balance of black and white modules
int black = 0;
for (int y = 0; y < qrsize; y++) {
@ -709,6 +712,26 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
}
// Inserts the given value to the front of the given array, which shifts over the
// existing values and deletes the last value. A helper function for getPenaltyScore().
static void addRunToHistory(unsigned char run, unsigned char history[7]) {
memmove(&history[1], &history[0], 6 * sizeof(history[0]));
history[0] = run;
}
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
static bool hasFinderLikePattern(unsigned char runHistory[7]) {
unsigned char n = runHistory[1];
// The maximum QR Code size is 177, hence the run length n <= 177.
// Arithmetic is promoted to int, so n*4 will not overflow.
return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n
&& runHistory[3] == n * 3 && (runHistory[0] >= n * 4 || runHistory[6] >= n * 4);
}
/*---- Basic QR Code information ----*/

@ -428,8 +428,9 @@ int QrCode::handleConstructorMasking(int mask) {
long QrCode::getPenaltyScore() const {
long result = 0;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for (int y = 0; y < size; y++) {
std::deque<int> runHistory(7, 0);
bool color = false;
int runX = 0;
for (int x = 0; x < size; x++) {
@ -440,13 +441,22 @@ long QrCode::getPenaltyScore() const {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = module(x, y);
runX = 1;
}
}
addRunToHistory(runX, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for (int x = 0; x < size; x++) {
std::deque<int> runHistory(7, 0);
bool color = false;
int runY = 0;
for (int y = 0; y < size; y++) {
@ -457,10 +467,18 @@ long QrCode::getPenaltyScore() const {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = module(x, y);
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -474,23 +492,6 @@ long QrCode::getPenaltyScore() const {
}
}
// Finder-like pattern in rows
for (int y = 0; y < size; y++) {
for (int x = 0, bits = 0; x < size; x++) {
bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0);
if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Finder-like pattern in columns
for (int x = 0; x < size; x++) {
for (int y = 0, bits = 0; y < size; y++) {
bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0);
if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Balance of black and white modules
int black = 0;
for (const vector<bool> &row : modules) {
@ -544,6 +545,19 @@ int QrCode::getNumDataCodewords(int ver, Ecc ecl) {
}
void QrCode::addRunToHistory(int run, std::deque<int> &history) {
history.pop_back();
history.push_front(run);
}
bool QrCode::hasFinderLikePattern(const std::deque<int> &runHistory) {
int n = runHistory.at(1);
return n > 0 && runHistory.at(2) == n && runHistory.at(4) == n && runHistory.at(5) == n
&& runHistory.at(3) == n * 3 && std::max(runHistory.at(0), runHistory.at(6)) >= n * 4;
}
bool QrCode::getBit(long x, int i) {
return ((x >> i) & 1) != 0;
}

@ -24,6 +24,7 @@
#pragma once
#include <cstdint>
#include <deque>
#include <string>
#include <vector>
#include "QrSegment.hpp"
@ -278,6 +279,17 @@ class QrCode final {
private: static int getNumDataCodewords(int ver, Ecc ecl);
// Inserts the given value to the front of the given array, which shifts over the
// existing values and deletes the last value. A helper function for getPenaltyScore().
private: static void addRunToHistory(int run, std::deque<int> &history);
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
private: static bool hasFinderLikePattern(const std::deque<int> &runHistory);
// Returns true iff the i'th bit of x is set to 1.
private: static bool getBit(long x, int i);

@ -595,8 +595,9 @@ public final class QrCode {
private int getPenaltyScore() {
int result = 0;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for (int y = 0; y < size; y++) {
int[] runHistory = new int[7];
boolean color = false;
int runX = 0;
for (int x = 0; x < size; x++) {
@ -607,13 +608,22 @@ public final class QrCode {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = modules[y][x];
runX = 1;
}
}
addRunToHistory(runX, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for (int x = 0; x < size; x++) {
int[] runHistory = new int[7];
boolean color = false;
int runY = 0;
for (int y = 0; y < size; y++) {
@ -624,10 +634,18 @@ public final class QrCode {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (!color && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
color = modules[y][x];
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (color)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -641,23 +659,6 @@ public final class QrCode {
}
}
// Finder-like pattern in rows
for (int y = 0; y < size; y++) {
for (int x = 0, bits = 0; x < size; x++) {
bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0);
if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Finder-like pattern in columns
for (int x = 0; x < size; x++) {
for (int y = 0, bits = 0; y < size; y++) {
bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0);
if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
// Balance of black and white modules
int black = 0;
for (boolean[] row : modules) {
@ -734,6 +735,24 @@ public final class QrCode {
}
// Inserts the given value to the front of the given array, which shifts over the
// existing values and deletes the last value. A helper function for getPenaltyScore().
private static void addRunToHistory(int run, int[] history) {
System.arraycopy(history, 0, history, 1, history.length - 1);
history[0] = run;
}
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
private static boolean hasFinderLikePattern(int[] runHistory) {
int n = runHistory[1];
return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n
&& runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4;
}
// Returns true iff the i'th bit of x is set to 1.
static boolean getBit(int x, int i) {
return ((x >>> i) & 1) != 0;

@ -427,8 +427,9 @@ var qrcodegen = new function() {
function getPenaltyScore() {
var result = 0;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for (var y = 0; y < size; y++) {
var runHistory = [0,0,0,0,0,0,0];
var color = false;
var runX = 0;
for (var x = 0; x < size; x++) {
@ -439,13 +440,22 @@ var qrcodegen = new function() {
else if (runX > 5)
result++;
} else {
QrCode.addRunToHistory(runX, runHistory);
if (!color && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
color = modules[y][x];
runX = 1;
}
}
QrCode.addRunToHistory(runX, runHistory);
if (color)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for (var x = 0; x < size; x++) {
var runHistory = [0,0,0,0,0,0,0];
var color = false;
var runY = 0;
for (var y = 0; y < size; y++) {
@ -456,10 +466,18 @@ var qrcodegen = new function() {
else if (runY > 5)
result++;
} else {
QrCode.addRunToHistory(runY, runHistory);
if (!color && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
color = modules[y][x];
runY = 1;
}
}
QrCode.addRunToHistory(runY, runHistory);
if (color)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -473,23 +491,6 @@ var qrcodegen = new function() {
}
}
// Finder-like pattern in rows
for (var y = 0; y < size; y++) {
for (var x = 0, bits = 0; x < size; x++) {
bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0);
if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += QrCode.PENALTY_N3;
}
}
// Finder-like pattern in columns
for (var x = 0; x < size; x++) {
for (var y = 0, bits = 0; y < size; y++) {
bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0);
if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated
result += QrCode.PENALTY_N3;
}
}
// Balance of black and white modules
var black = 0;
modules.forEach(function(row) {
@ -666,6 +667,24 @@ var qrcodegen = new function() {
};
// Inserts the given value to the front of the given array, which shifts over the
// existing values and deletes the last value. A helper function for getPenaltyScore().
QrCode.addRunToHistory = function(run, history) {
history.pop();
history.unshift(run);
};
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
QrCode.hasFinderLikePattern = function(runHistory) {
var n = runHistory[1];
return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n
&& runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4;
};
/*---- Constants and tables for QrCode ----*/
var MIN_VERSION = 1; // The minimum version number supported in the QR Code Model 2 standard

@ -21,7 +21,7 @@
# Software.
#
import itertools, re, sys
import collections, itertools, re, sys
"""
@ -462,8 +462,9 @@ class QrCode(object):
size = self._size
modules = self._modules
# Adjacent modules in row having same color
# Adjacent modules in row having same color, and finder-like patterns
for y in range(size):
runhistory = collections.deque([0] * 7, 7)
color = False
runx = 0
for x in range(size):
@ -474,10 +475,19 @@ class QrCode(object):
elif runx > 5:
result += 1
else:
runhistory.appendleft(runx)
if not color and QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
color = modules[y][x]
runx = 1
# Adjacent modules in column having same color
runhistory.appendleft(runx)
if color:
runhistory.appendleft(0) # Dummy run of white
if QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
# Adjacent modules in column having same color, and finder-like patterns
for x in range(size):
runhistory = collections.deque([0] * 7, 7)
color = False
runy = 0
for y in range(size):
@ -488,8 +498,16 @@ class QrCode(object):
elif runy > 5:
result += 1
else:
runhistory.appendleft(runy)
if not color and QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
color = modules[y][x]
runy = 1
runhistory.appendleft(runy)
if color:
runhistory.appendleft(0) # Dummy run of white
if QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
# 2*2 blocks of modules having same color
for y in range(size - 1):
@ -497,21 +515,6 @@ class QrCode(object):
if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]:
result += QrCode._PENALTY_N2
# Finder-like pattern in rows
for y in range(size):
bits = 0
for x in range(size):
bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0)
if x >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated
result += QrCode._PENALTY_N3
# Finder-like pattern in columns
for x in range(size):
bits = 0
for y in range(size):
bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0)
if y >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated
result += QrCode._PENALTY_N3
# Balance of black and white modules
black = sum((1 if cell else 0) for row in modules for cell in row)
total = size**2 # Note that size is odd, so black/total != 1/2
@ -564,6 +567,13 @@ class QrCode(object):
* QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]
@staticmethod
def has_finder_like_pattern(runhistory):
n = runhistory[1]
return n > 0 and n == runhistory[2] == runhistory[4] == runhistory[5] \
and runhistory[3] == n * 3 and max(runhistory[0], runhistory[6]) >= n * 4
# ---- Constants and tables ----
MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard

@ -645,8 +645,9 @@ impl QrCode {
let mut result: i32 = 0;
let size: i32 = self.size;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for y in 0 .. size {
let mut runhistory = RunHistory::new();
let mut color = false;
let mut runx: i32 = 0;
for x in 0 .. size {
@ -658,13 +659,25 @@ impl QrCode {
result += 1;
}
} else {
runhistory.add_run(runx);
if !color && runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
color = self.module(x, y);
runx = 1;
}
}
runhistory.add_run(runx);
if color {
runhistory.add_run(0); // Dummy run of white
}
if runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for x in 0 .. size {
let mut runhistory = RunHistory::new();
let mut color = false;
let mut runy: i32 = 0;
for y in 0 .. size {
@ -676,10 +689,21 @@ impl QrCode {
result += 1;
}
} else {
runhistory.add_run(runy);
if !color && runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
color = self.module(x, y);
runy = 1;
}
}
runhistory.add_run(runy);
if color {
runhistory.add_run(0); // Dummy run of white
}
if runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
}
// 2*2 blocks of modules having same color
@ -694,27 +718,6 @@ impl QrCode {
}
}
// Finder-like pattern in rows
for y in 0 .. size {
let mut bits: u32 = 0;
for x in 0 .. size {
bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32);
if x >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
}
// Finder-like pattern in columns
for x in 0 .. size {
let mut bits: u32 = 0;
for y in 0 .. size {
bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32);
if y >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated
result += PENALTY_N3;
}
}
}
// Balance of black and white modules
let mut black: i32 = 0;
for color in &self.modules {
@ -939,6 +942,41 @@ impl ReedSolomonGenerator {
/*---- RunHistory functionality ----*/
struct RunHistory(std::collections::VecDeque<i32>);
impl RunHistory {
fn new() -> Self {
let mut temp = std::collections::VecDeque::<i32>::new();
temp.resize(7, 0);
RunHistory(temp)
}
// Inserts the given value to the front of this array, which shifts over the existing
// values and deletes the last value. A helper function for get_penalty_score().
fn add_run(&mut self, run: i32) {
self.0.pop_back();
self.0.push_front(run);
}
// Tests whether this run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for get_penalty_score().
// Must only be called immediately after a run of white modules has ended.
fn has_finder_like_pattern(&self) -> bool {
let n = self.0[1];
n > 0 && self.0[2] == n && self.0[4] == n && self.0[5] == n
&& self.0[3] == n * 3 && std::cmp::max(self.0[0], self.0[6]) >= n * 4
}
}
/*---- QrSegment functionality ----*/
/// A segment of character/binary/control data in a QR Code symbol.

@ -510,8 +510,9 @@ namespace qrcodegen {
private getPenaltyScore(): int {
let result: int = 0;
// Adjacent modules in row having same color
// Adjacent modules in row having same color, and finder-like patterns
for (let y = 0; y < this.size; y++) {
let runHistory = [0,0,0,0,0,0,0];
let color = false;
let runX = 0;
for (let x = 0; x < this.size; x++) {
@ -522,13 +523,22 @@ namespace qrcodegen {
else if (runX > 5)
result++;
} else {
QrCode.addRunToHistory(runX, runHistory);
if (!color && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
color = this.modules[y][x];
runX = 1;
}
}
QrCode.addRunToHistory(runX, runHistory);
if (color)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
}
// Adjacent modules in column having same color
// Adjacent modules in column having same color, and finder-like patterns
for (let x = 0; x < this.size; x++) {
let runHistory = [0,0,0,0,0,0,0];
let color = false;
let runY = 0;
for (let y = 0; y < this.size; y++) {
@ -539,10 +549,18 @@ namespace qrcodegen {
else if (runY > 5)
result++;
} else {
QrCode.addRunToHistory(runY, runHistory);
if (!color && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
color = this.modules[y][x];
runY = 1;
}
}
QrCode.addRunToHistory(runY, runHistory);
if (color)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -556,23 +574,6 @@ namespace qrcodegen {
}
}
// Finder-like pattern in rows
for (let y = 0; y < this.size; y++) {
for (let x = 0, bits = 0; x < this.size; x++) {
bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0);
if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated
result += QrCode.PENALTY_N3;
}
}
// Finder-like pattern in columns
for (let x = 0; x < this.size; x++) {
for (let y = 0, bits = 0; y < this.size; y++) {
bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0);
if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated
result += QrCode.PENALTY_N3;
}
}
// Balance of black and white modules
let black: int = 0;
for (let row of this.modules) {
@ -636,6 +637,24 @@ namespace qrcodegen {
}
// Inserts the given value to the front of the given array, which shifts over the
// existing values and deletes the last value. A helper function for getPenaltyScore().
private static addRunToHistory(run: int, history: Array<int>): void {
history.pop();
history.unshift(run);
}
// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and
// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore().
// Must only be called immediately after a run of white modules has ended.
private static hasFinderLikePattern(runHistory: Array<int>): boolean {
const n: int = runHistory[1];
return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n
&& runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4;
}
/*-- Constants and tables --*/
// The minimum version number supported in the QR Code Model 2 standard.

Loading…
Cancel
Save