|
|
|
@ -481,9 +481,10 @@ public final class QrCode {
|
|
|
|
|
|
|
|
|
|
// Iterate over adjacent pairs of rows
|
|
|
|
|
for (int index = 0, downIndex = size, end = size * size; index < end; ) {
|
|
|
|
|
Arrays.fill(runHistory, 0);
|
|
|
|
|
int runColor = 0;
|
|
|
|
|
int runX = 0;
|
|
|
|
|
Arrays.fill(runHistory, 0);
|
|
|
|
|
int padRun = size; // Add white border to initial run
|
|
|
|
|
int curRow = 0;
|
|
|
|
|
int nextRow = 0;
|
|
|
|
|
for (int x = 0; x < size; x++, index++, downIndex++) {
|
|
|
|
@ -495,9 +496,10 @@ public final class QrCode {
|
|
|
|
|
else if (runX > 5)
|
|
|
|
|
result++;
|
|
|
|
|
} else {
|
|
|
|
|
addRunToHistory(runX, runHistory);
|
|
|
|
|
if (runColor == 0 && hasFinderLikePattern(runHistory))
|
|
|
|
|
result += PENALTY_N3;
|
|
|
|
|
finderPenaltyAddHistory(runX + padRun, runHistory);
|
|
|
|
|
padRun = 0;
|
|
|
|
|
if (runColor == 0)
|
|
|
|
|
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
|
|
|
|
|
runColor = c;
|
|
|
|
|
runX = 1;
|
|
|
|
|
}
|
|
|
|
@ -510,18 +512,15 @@ public final class QrCode {
|
|
|
|
|
result += PENALTY_N2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addRunToHistory(runX, runHistory);
|
|
|
|
|
if (runColor == 1)
|
|
|
|
|
addRunToHistory(0, runHistory); // Dummy run of white
|
|
|
|
|
if (hasFinderLikePattern(runHistory))
|
|
|
|
|
result += PENALTY_N3;
|
|
|
|
|
result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterate over single columns
|
|
|
|
|
for (int x = 0; x < size; x++) {
|
|
|
|
|
Arrays.fill(runHistory, 0);
|
|
|
|
|
int runColor = 0;
|
|
|
|
|
int runY = 0;
|
|
|
|
|
Arrays.fill(runHistory, 0);
|
|
|
|
|
int padRun = size; // Add white border to initial run
|
|
|
|
|
for (int y = 0, index = x; y < size; y++, index += size) {
|
|
|
|
|
int c = getBit(modules[index >>> 5], index);
|
|
|
|
|
if (c == runColor) {
|
|
|
|
@ -531,18 +530,15 @@ public final class QrCode {
|
|
|
|
|
else if (runY > 5)
|
|
|
|
|
result++;
|
|
|
|
|
} else {
|
|
|
|
|
addRunToHistory(runY, runHistory);
|
|
|
|
|
if (runColor == 0 && hasFinderLikePattern(runHistory))
|
|
|
|
|
result += PENALTY_N3;
|
|
|
|
|
finderPenaltyAddHistory(runY + padRun, runHistory);
|
|
|
|
|
padRun = 0;
|
|
|
|
|
if (runColor == 0)
|
|
|
|
|
result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
|
|
|
|
|
runColor = c;
|
|
|
|
|
runY = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addRunToHistory(runY, runHistory);
|
|
|
|
|
if (runColor == 1)
|
|
|
|
|
addRunToHistory(0, runHistory); // Dummy run of white
|
|
|
|
|
if (hasFinderLikePattern(runHistory))
|
|
|
|
|
result += PENALTY_N3;
|
|
|
|
|
result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Balance of black and white modules
|
|
|
|
@ -567,21 +563,33 @@ 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;
|
|
|
|
|
// 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(int[] runHistory) {
|
|
|
|
|
int n = runHistory[1];
|
|
|
|
|
assert n <= size * 3;
|
|
|
|
|
boolean 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().
|
|
|
|
|
private int finderPenaltyTerminateAndCount(int currentRunColor, int currentRunLength, int[] runHistory) {
|
|
|
|
|
if (currentRunColor == 1) { // Terminate black run
|
|
|
|
|
finderPenaltyAddHistory(currentRunLength, runHistory);
|
|
|
|
|
currentRunLength = 0;
|
|
|
|
|
}
|
|
|
|
|
currentRunLength += size; // Add white border to final run
|
|
|
|
|
finderPenaltyAddHistory(currentRunLength, runHistory);
|
|
|
|
|
return finderPenaltyCountPatterns(runHistory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
|
|
|
|
|
private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) {
|
|
|
|
|
System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1);
|
|
|
|
|
runHistory[0] = currentRunLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|