mirror of
https://gitee.com/openharmony/third_party_qrcodegen
synced 2024-11-23 07:10:22 +00:00
Updated the finder pattern detector logic in the other 6 language versions to match Java code.
This commit is contained in:
parent
6794ebefa7
commit
c5ad557eea
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 ----
|
||||
|
103
rust/src/lib.rs
103
rust/src/lib.rs
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user