Updated the finder pattern detector logic in the other 6 language versions to match Java code.

This commit is contained in:
Project Nayuki 2019-07-06 03:30:51 +00:00
parent 6794ebefa7
commit c5ad557eea
7 changed files with 248 additions and 201 deletions

View File

@ -72,8 +72,9 @@ 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(const unsigned char runHistory[7]);
static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize);
static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize);
static void finderPenaltyAddHistory(int currentRunLength, int 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);
@ -634,9 +635,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
// Adjacent modules in row having same color, and finder-like patterns
for (int y = 0; y < qrsize; y++) {
unsigned char runHistory[7] = {0};
bool runColor = false;
unsigned char runX = 0;
int runX = 0;
int runHistory[7] = {0};
int padRun = qrsize; // Add white border to initial run
for (int x = 0; x < qrsize; x++) {
if (getModule(qrcode, x, y) == runColor) {
runX++;
@ -645,24 +647,22 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (!runColor && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3;
runColor = getModule(qrcode, x, y);
runX = 1;
}
}
addRunToHistory(runX, runHistory);
if (runColor)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory, qrsize) * PENALTY_N3;
}
// Adjacent modules in column having same color, and finder-like patterns
for (int x = 0; x < qrsize; x++) {
unsigned char runHistory[7] = {0};
bool runColor = false;
unsigned char runY = 0;
int runY = 0;
int runHistory[7] = {0};
int padRun = qrsize; // Add white border to initial run
for (int y = 0; y < qrsize; y++) {
if (getModule(qrcode, x, y) == runColor) {
runY++;
@ -671,18 +671,15 @@ static long getPenaltyScore(const uint8_t qrcode[]) {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (!runColor && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3;
runColor = getModule(qrcode, x, y);
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (runColor)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory, qrsize) * PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -712,23 +709,35 @@ 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;
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) {
int n = runHistory[1];
assert(n <= qrsize * 3);
bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
// The maximum QR Code size is 177, hence the black run length n <= 177.
// Arithmetic is promoted to int, so n*4 will not overflow.
return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
+ (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
}
// 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(const 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);
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) {
if (currentRunColor) { // Terminate black run
finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += qrsize; // Add white border to final run
finderPenaltyAddHistory(currentRunLength, runHistory);
return finderPenaltyCountPatterns(runHistory, qrsize);
}
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7]) {
memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0]));
runHistory[0] = currentRunLength;
}

View File

@ -427,9 +427,10 @@ long QrCode::getPenaltyScore() const {
// 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 runColor = false;
int runX = 0;
std::array<int,7> runHistory = {};
int padRun = size; // Add white border to initial run
for (int x = 0; x < size; x++) {
if (module(x, y) == runColor) {
runX++;
@ -438,24 +439,22 @@ long QrCode::getPenaltyScore() const {
else if (runX > 5)
result++;
} else {
addRunToHistory(runX, runHistory);
if (!runColor && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = module(x, y);
runX = 1;
}
}
addRunToHistory(runX, runHistory);
if (runColor)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3;
}
// 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 runColor = false;
int runY = 0;
std::array<int,7> runHistory = {};
int padRun = size; // Add white border to initial run
for (int y = 0; y < size; y++) {
if (module(x, y) == runColor) {
runY++;
@ -464,18 +463,15 @@ long QrCode::getPenaltyScore() const {
else if (runY > 5)
result++;
} else {
addRunToHistory(runY, runHistory);
if (!runColor && hasFinderLikePattern(runHistory))
result += PENALTY_N3;
finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
runColor = module(x, y);
runY = 1;
}
}
addRunToHistory(runY, runHistory);
if (runColor)
addRunToHistory(0, runHistory); // Dummy run of white
if (hasFinderLikePattern(runHistory))
result += PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -542,16 +538,30 @@ int QrCode::getNumDataCodewords(int ver, Ecc ecl) {
}
void QrCode::addRunToHistory(int run, std::deque<int> &history) {
history.pop_back();
history.push_front(run);
int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const {
int n = runHistory.at(1);
if (n > size * 3)
throw std::logic_error("Assertion error");
bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n;
return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0)
+ (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0);
}
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;
int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const {
if (currentRunColor) { // Terminate black run
finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += size; // Add white border to final run
finderPenaltyAddHistory(currentRunLength, runHistory);
return finderPenaltyCountPatterns(runHistory);
}
void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) {
std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end());
runHistory.at(0) = currentRunLength;
}

View File

@ -23,8 +23,8 @@
#pragma once
#include <array>
#include <cstdint>
#include <deque>
#include <stdexcept>
#include <string>
#include <vector>
@ -274,15 +274,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);
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
private: int finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const;
// 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);
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const;
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
private: static void finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory);
// Returns true iff the i'th bit of x is set to 1.

View File

@ -429,9 +429,10 @@ var qrcodegen = new function() {
// 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 runColor = false;
var runX = 0;
var runHistory = [0,0,0,0,0,0,0];
var padRun = size;
for (var x = 0; x < size; x++) {
if (modules[y][x] == runColor) {
runX++;
@ -440,24 +441,22 @@ var qrcodegen = new function() {
else if (runX > 5)
result++;
} else {
QrCode.addRunToHistory(runX, runHistory);
if (!runColor && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
QrCode.finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = modules[y][x];
runX = 1;
}
}
QrCode.addRunToHistory(runX, runHistory);
if (runColor)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3;
}
// 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 runColor = false;
var runY = 0;
var runHistory = [0,0,0,0,0,0,0];
var padRun = size;
for (var y = 0; y < size; y++) {
if (modules[y][x] == runColor) {
runY++;
@ -466,18 +465,15 @@ var qrcodegen = new function() {
else if (runY > 5)
result++;
} else {
QrCode.addRunToHistory(runY, runHistory);
if (!runColor && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
QrCode.finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (!runColor)
result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = modules[y][x];
runY = 1;
}
}
QrCode.addRunToHistory(runY, runHistory);
if (runColor)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -525,6 +521,30 @@ var qrcodegen = new function() {
}
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
function finderPenaltyCountPatterns(runHistory) {
var n = runHistory[1];
if (n > size * 3)
throw "Assertion error";
var core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
+ (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
}
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
function finderPenaltyTerminateAndCount(currentRunColor, currentRunLength, runHistory) {
if (currentRunColor) { // Terminate black run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += size; // Add white border to final run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
return finderPenaltyCountPatterns(runHistory);
}
// Returns true iff the i'th bit of x is set to 1.
function getBit(x, i) {
return ((x >>> i) & 1) != 0;
@ -667,11 +687,10 @@ 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);
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
QrCode.finderPenaltyAddHistory = function(currentRunLength, runHistory) {
runHistory.pop();
runHistory.unshift(currentRunLength);
};

View File

@ -464,9 +464,10 @@ class QrCode(object):
# Adjacent modules in row having same color, and finder-like patterns
for y in range(size):
runhistory = collections.deque([0] * 7, 7)
runcolor = False
runx = 0
runhistory = collections.deque([0] * 7, 7)
padrun = size # Add white border to initial run
for x in range(size):
if modules[y][x] == runcolor:
runx += 1
@ -475,21 +476,19 @@ class QrCode(object):
elif runx > 5:
result += 1
else:
runhistory.appendleft(runx)
if not runcolor and QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
runhistory.appendleft(runx + padrun)
padrun = 0
if not runcolor:
result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3
runcolor = modules[y][x]
runx = 1
runhistory.appendleft(runx)
if runcolor:
runhistory.appendleft(0) # Dummy run of white
if QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
result += self._finder_penalty_terminate_and_count(runcolor, runx + padrun, runhistory) * 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)
runcolor = False
runy = 0
runhistory = collections.deque([0] * 7, 7)
padrun = size # Add white border to initial run
for y in range(size):
if modules[y][x] == runcolor:
runy += 1
@ -498,16 +497,13 @@ class QrCode(object):
elif runy > 5:
result += 1
else:
runhistory.appendleft(runy)
if not runcolor and QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
runhistory.appendleft(runy + padrun)
padrun = 0
if not runcolor:
result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3
runcolor = modules[y][x]
runy = 1
runhistory.appendleft(runy)
if runcolor:
runhistory.appendleft(0) # Dummy run of white
if QrCode.has_finder_like_pattern(runhistory):
result += QrCode._PENALTY_N3
result += self._finder_penalty_terminate_and_count(runcolor, runy + padrun, runhistory) * QrCode._PENALTY_N3
# 2*2 blocks of modules having same color
for y in range(size - 1):
@ -567,11 +563,24 @@ class QrCode(object):
* QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]
@staticmethod
def has_finder_like_pattern(runhistory):
# Can only be called immediately after a white run is added, and
# returns either 0, 1, or 2. A helper function for _get_penalty_score().
def _finder_penalty_count_patterns(self, 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
assert n <= self._size * 3
core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3
return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \
+ (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0)
# Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score().
def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory):
if currentruncolor: # Terminate black run
runhistory.appendleft(currentrunlength)
currentrunlength = 0
currentrunlength += self._size # Add white border to final run
runhistory.appendleft(currentrunlength)
return self._finder_penalty_count_patterns(runhistory)
# ---- Constants and tables ----

View File

@ -642,9 +642,10 @@ impl QrCode {
// Adjacent modules in row having same color, and finder-like patterns
for y in 0 .. size {
let mut runhistory = RunHistory::new();
let mut runcolor = false;
let mut runx: i32 = 0;
let mut runhistory = [0i32; 7];
let mut padrun = size; // Add white border to initial run
for x in 0 .. size {
if self.module(x, y) == runcolor {
runx += 1;
@ -654,27 +655,23 @@ impl QrCode {
result += 1;
}
} else {
runhistory.add_run(runx);
if !runcolor && runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
QrCode::finder_penalty_add_history(runx + padrun, &mut runhistory);
padrun = 0;
if !runcolor {
result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3;
}
runcolor = self.module(x, y);
runx = 1;
}
}
runhistory.add_run(runx);
if runcolor {
runhistory.add_run(0); // Dummy run of white
}
if runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
result += self.finder_penalty_terminate_and_count(runcolor, runx + padrun, &mut runhistory) * PENALTY_N3;
}
// Adjacent modules in column having same color, and finder-like patterns
for x in 0 .. size {
let mut runhistory = RunHistory::new();
let mut runcolor = false;
let mut runy: i32 = 0;
let mut runhistory = [0i32; 7];
let mut padrun = size; // Add white border to initial run
for y in 0 .. size {
if self.module(x, y) == runcolor {
runy += 1;
@ -684,21 +681,16 @@ impl QrCode {
result += 1;
}
} else {
runhistory.add_run(runy);
if !runcolor && runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
QrCode::finder_penalty_add_history(runy + padrun, &mut runhistory);
padrun = 0;
if !runcolor {
result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3;
}
runcolor = self.module(x, y);
runy = 1;
}
}
runhistory.add_run(runy);
if runcolor {
runhistory.add_run(0); // Dummy run of white
}
if runhistory.has_finder_like_pattern() {
result += PENALTY_N3;
}
result += self.finder_penalty_terminate_and_count(runcolor, runy + padrun, &mut runhistory) * PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -780,6 +772,38 @@ impl QrCode {
table[ecl.ordinal()][ver.value() as usize] as usize
}
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for get_penalty_score().
fn finder_penalty_count_patterns(&self, runhistory: &[i32;7]) -> i32 {
let n = runhistory[1];
assert!(n <= self.size * 3);
let core = n > 0 && runhistory[2] == n && runhistory[3] == n * 3 && runhistory[4] == n && runhistory[5] == n;
return if core && runhistory[0] >= n * 4 && runhistory[6] >= n { 1 } else { 0 }
+ if core && runhistory[6] >= n * 4 && runhistory[0] >= n { 1 } else { 0 };
}
// Must be called at the end of a line (row or column) of modules. A helper function for get_penalty_score().
fn finder_penalty_terminate_and_count(&self, currentruncolor: bool, mut currentrunlength: i32, runhistory: &mut [i32;7]) -> i32 {
if currentruncolor { // Terminate black run
QrCode::finder_penalty_add_history(currentrunlength, runhistory);
currentrunlength = 0;
}
currentrunlength += self.size; // Add white border to final run
QrCode::finder_penalty_add_history(currentrunlength, runhistory);
self.finder_penalty_count_patterns(runhistory)
}
// Pushes the given value to the front and drops the last value. A helper function for get_penalty_score().
fn finder_penalty_add_history(currentrunlength: i32, runhistory: &mut [i32;7]) {
for i in (0 .. runhistory.len()-1).rev() {
runhistory[i + 1] = runhistory[i];
}
runhistory[0] = currentrunlength;
}
}
@ -937,41 +961,6 @@ 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.

View File

@ -512,9 +512,10 @@ namespace qrcodegen {
// 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 runColor = false;
let runX = 0;
let runHistory = [0,0,0,0,0,0,0];
let padRun = this.size;
for (let x = 0; x < this.size; x++) {
if (this.modules[y][x] == runColor) {
runX++;
@ -523,24 +524,22 @@ namespace qrcodegen {
else if (runX > 5)
result++;
} else {
QrCode.addRunToHistory(runX, runHistory);
if (!runColor && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
QrCode.finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (!runColor)
result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = this.modules[y][x];
runX = 1;
}
}
QrCode.addRunToHistory(runX, runHistory);
if (runColor)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
result += this.finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3;
}
// 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 runColor = false;
let runY = 0;
let runHistory = [0,0,0,0,0,0,0];
let padRun = this.size;
for (let y = 0; y < this.size; y++) {
if (this.modules[y][x] == runColor) {
runY++;
@ -549,18 +548,15 @@ namespace qrcodegen {
else if (runY > 5)
result++;
} else {
QrCode.addRunToHistory(runY, runHistory);
if (!runColor && QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
QrCode.finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (!runColor)
result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = this.modules[y][x];
runY = 1;
}
}
QrCode.addRunToHistory(runY, runHistory);
if (runColor)
QrCode.addRunToHistory(0, runHistory); // Dummy run of white
if (QrCode.hasFinderLikePattern(runHistory))
result += QrCode.PENALTY_N3;
result += this.finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3;
}
// 2*2 blocks of modules having same color
@ -637,21 +633,34 @@ 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);
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
private finderPenaltyCountPatterns(runHistory: Array<int>): int {
const n: int = runHistory[1];
if (n > this.size * 3)
throw "Assertion error";
const core: boolean = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
+ (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
}
// 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;
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
private finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array<int>): int {
if (currentRunColor) { // Terminate black run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += this.size; // Add white border to final run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
return this.finderPenaltyCountPatterns(runHistory);
}
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
private static finderPenaltyAddHistory(currentRunLength: int, runHistory: Array<int>): void {
runHistory.pop();
runHistory.unshift(currentRunLength);
}