From f05a8f9098bd1bbad12b04bf87d783bb4f192c99 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 21 Jul 2019 00:04:52 +0000 Subject: [PATCH] Replaced the finder-like pattern detection algorithm with a more sophisticated and accurate one, synchronizing with the parent project. --- src/io/nayuki/fastqrcodegen/QrCode.java | 68 ++++++++++++++----------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 1928014..b629391 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -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; }