Simplified pieces of code (#601)

* Simplify code in bit-twiddling methods, using APIs introduced in Java 5.

* Align code by adjusting whitespace and adding `+0`.

* Convert qrcode DataMask to enum to reduce source code size, which shouldn't change behavior.

* Simplify lists of character constants using string initializer instead of array.

* Remove redundant `+ 0` operations from a recent change.

* Delete the function DataMask.forReference() and inline the logic into all its callers.
This commit is contained in:
Nayuki 2016-05-25 11:46:44 +00:00 committed by Sean Owen
parent c2eb20d976
commit 7cf49565c2
9 changed files with 110 additions and 160 deletions

View file

@ -64,15 +64,11 @@ final class DecodedBitStreamParser {
private static final int PS = 29; private static final int PS = 29;
private static final int PAL = 29; private static final int PAL = 29;
private static final char[] PUNCT_CHARS = { private static final char[] PUNCT_CHARS =
';', '<', '>', '@', '[', '\\', ']', '_', '`', '~', '!', ";<>@[\\]_`~!\r\t,:\n-.$/\"|*()?{}'".toCharArray();
'\r', '\t', ',', ':', '\n', '-', '.', '$', '/', '"', '|', '*',
'(', ')', '?', '{', '}', '\''};
private static final char[] MIXED_CHARS = { private static final char[] MIXED_CHARS =
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&', "0123456789&\r\t,:#-.$/+%*=^".toCharArray();
'\r', '\t', ',', ':', '#', '-', '.', '$', '/', '+', '%', '*',
'=', '^'};
private static final Charset DEFAULT_ENCODING = Charset.forName("ISO-8859-1"); private static final Charset DEFAULT_ENCODING = Charset.forName("ISO-8859-1");

View file

@ -157,7 +157,7 @@ final class BitMatrixParser {
// Get the data mask for the format used in this QR Code. This will exclude // Get the data mask for the format used in this QR Code. This will exclude
// some bits from reading as we wind through the bit matrix. // some bits from reading as we wind through the bit matrix.
DataMask dataMask = DataMask.forReference(formatInfo.getDataMask()); DataMask dataMask = DataMask.values()[formatInfo.getDataMask()];
int dimension = bitMatrix.getHeight(); int dimension = bitMatrix.getHeight();
dataMask.unmaskBitMatrix(bitMatrix, dimension); dataMask.unmaskBitMatrix(bitMatrix, dimension);
@ -211,7 +211,7 @@ final class BitMatrixParser {
if (parsedFormatInfo == null) { if (parsedFormatInfo == null) {
return; // We have no format information, and have no data mask return; // We have no format information, and have no data mask
} }
DataMask dataMask = DataMask.forReference(parsedFormatInfo.getDataMask()); DataMask dataMask = DataMask.values()[parsedFormatInfo.getDataMask()];
int dimension = bitMatrix.getHeight(); int dimension = bitMatrix.getHeight();
dataMask.unmaskBitMatrix(bitMatrix, dimension); dataMask.unmaskBitMatrix(bitMatrix, dimension);
} }

View file

@ -29,24 +29,96 @@ import com.google.zxing.common.BitMatrix;
* *
* @author Sean Owen * @author Sean Owen
*/ */
abstract class DataMask { enum DataMask {
/** /**
* See ISO 18004:2006 6.8.1 * See ISO 18004:2006 6.8.1
*/ */
private static final DataMask[] DATA_MASKS = {
new DataMask000(), /**
new DataMask001(), * 000: mask bits for which (x + y) mod 2 == 0
new DataMask010(), */
new DataMask011(), DATA_MASK_000() {
new DataMask100(), @Override
new DataMask101(), boolean isMasked(int i, int j) {
new DataMask110(), return ((i + j) & 0x01) == 0;
new DataMask111(), }
},
/**
* 001: mask bits for which x mod 2 == 0
*/
DATA_MASK_001() {
@Override
boolean isMasked(int i, int j) {
return (i & 0x01) == 0;
}
},
/**
* 010: mask bits for which y mod 3 == 0
*/
DATA_MASK_010() {
@Override
boolean isMasked(int i, int j) {
return j % 3 == 0;
}
},
/**
* 011: mask bits for which (x + y) mod 3 == 0
*/
DATA_MASK_011() {
@Override
boolean isMasked(int i, int j) {
return (i + j) % 3 == 0;
}
},
/**
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
*/
DATA_MASK_100() {
@Override
boolean isMasked(int i, int j) {
return (((i / 2) + (j /3)) & 0x01) == 0;
}
},
/**
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
*/
DATA_MASK_101() {
@Override
boolean isMasked(int i, int j) {
int temp = i * j;
return (temp & 0x01) + (temp % 3) == 0;
}
},
/**
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
*/
DATA_MASK_110() {
@Override
boolean isMasked(int i, int j) {
int temp = i * j;
return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
}
},
/**
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
*/
DATA_MASK_111() {
@Override
boolean isMasked(int i, int j) {
return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
}
}; };
private DataMask() { // End of enum constants.
}
/** /**
* <p>Implementations of this method reverse the data masking process applied to a QR Code and * <p>Implementations of this method reverse the data masking process applied to a QR Code and
@ -67,97 +139,4 @@ abstract class DataMask {
abstract boolean isMasked(int i, int j); abstract boolean isMasked(int i, int j);
/**
* @param reference a value between 0 and 7 indicating one of the eight possible
* data mask patterns a QR Code may use
* @return DataMask encapsulating the data mask pattern
*/
static DataMask forReference(int reference) {
if (reference < 0 || reference > 7) {
throw new IllegalArgumentException();
}
return DATA_MASKS[reference];
}
/**
* 000: mask bits for which (x + y) mod 2 == 0
*/
private static final class DataMask000 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return ((i + j) & 0x01) == 0;
}
}
/**
* 001: mask bits for which x mod 2 == 0
*/
private static final class DataMask001 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return (i & 0x01) == 0;
}
}
/**
* 010: mask bits for which y mod 3 == 0
*/
private static final class DataMask010 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return j % 3 == 0;
}
}
/**
* 011: mask bits for which (x + y) mod 3 == 0
*/
private static final class DataMask011 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return (i + j) % 3 == 0;
}
}
/**
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
*/
private static final class DataMask100 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return (((i / 2) + (j /3)) & 0x01) == 0;
}
}
/**
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
*/
private static final class DataMask101 extends DataMask {
@Override
boolean isMasked(int i, int j) {
int temp = i * j;
return (temp & 0x01) + (temp % 3) == 0;
}
}
/**
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
*/
private static final class DataMask110 extends DataMask {
@Override
boolean isMasked(int i, int j) {
int temp = i * j;
return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
}
}
/**
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
*/
private static final class DataMask111 extends DataMask {
@Override
boolean isMasked(int i, int j) {
return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
}
}
} }

View file

@ -42,12 +42,8 @@ final class DecodedBitStreamParser {
/** /**
* See ISO 18004:2006, 6.4.4 Table 5 * See ISO 18004:2006, 6.4.4 Table 5
*/ */
private static final char[] ALPHANUMERIC_CHARS = { private static final char[] ALPHANUMERIC_CHARS =
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".toCharArray();
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
' ', '$', '%', '*', '+', '-', '.', '/', ':'
};
private static final int GB2312_SUBSET = 1; private static final int GB2312_SUBSET = 1;
private DecodedBitStreamParser() { private DecodedBitStreamParser() {

View file

@ -66,12 +66,6 @@ final class FormatInformation {
{0x2BED, 0x1F}, {0x2BED, 0x1F},
}; };
/**
* Offset i holds the number of 1 bits in the binary representation of i
*/
private static final int[] BITS_SET_IN_HALF_BYTE =
{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
private final ErrorCorrectionLevel errorCorrectionLevel; private final ErrorCorrectionLevel errorCorrectionLevel;
private final byte dataMask; private final byte dataMask;
@ -83,17 +77,7 @@ final class FormatInformation {
} }
static int numBitsDiffering(int a, int b) { static int numBitsDiffering(int a, int b) {
a ^= b; // a now has a 1 bit exactly where its bit differs with b's return Integer.bitCount(a ^ b);
// Count bits set quickly with a series of lookups:
return
BITS_SET_IN_HALF_BYTE[a & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 4) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 8) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 12) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 16) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 20) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 24) & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 28) & 0x0F];
} }
/** /**

View file

@ -77,23 +77,23 @@ final class MaskUtil {
byte[] arrayY = array[y]; // We can at least optimize this access byte[] arrayY = array[y]; // We can at least optimize this access
if (x + 6 < width && if (x + 6 < width &&
arrayY[x] == 1 && arrayY[x] == 1 &&
arrayY[x + 1] == 0 && arrayY[x + 1] == 0 &&
arrayY[x + 2] == 1 && arrayY[x + 2] == 1 &&
arrayY[x + 3] == 1 && arrayY[x + 3] == 1 &&
arrayY[x + 4] == 1 && arrayY[x + 4] == 1 &&
arrayY[x + 5] == 0 && arrayY[x + 5] == 0 &&
arrayY[x + 6] == 1 && arrayY[x + 6] == 1 &&
(isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) { (isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) {
numPenalties++; numPenalties++;
} }
if (y + 6 < height && if (y + 6 < height &&
array[y][x] == 1 && array[y][x] == 1 &&
array[y + 1][x] == 0 && array[y + 1][x] == 0 &&
array[y + 2][x] == 1 && array[y + 2][x] == 1 &&
array[y + 3][x] == 1 && array[y + 3][x] == 1 &&
array[y + 4][x] == 1 && array[y + 4][x] == 1 &&
array[y + 5][x] == 0 && array[y + 5][x] == 0 &&
array[y + 6][x] == 1 && array[y + 6][x] == 1 &&
(isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) { (isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) {
numPenalties++; numPenalties++;
} }

View file

@ -271,12 +271,7 @@ final class MatrixUtil {
// - findMSBSet(1) => 1 // - findMSBSet(1) => 1
// - findMSBSet(255) => 8 // - findMSBSet(255) => 8
static int findMSBSet(int value) { static int findMSBSet(int value) {
int numDigits = 0; return 32 - Integer.numberOfLeadingZeros(value);
while (value != 0) {
value >>>= 1;
++numDigits;
}
return numDigits;
} }
// Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH

View file

@ -106,7 +106,7 @@ public final class DataMaskTestCase extends Assert {
} }
private static void testMaskAcrossDimensions(int reference, MaskCondition condition) { private static void testMaskAcrossDimensions(int reference, MaskCondition condition) {
DataMask mask = DataMask.forReference(reference); DataMask mask = DataMask.values()[reference];
for (int version = 1; version <= 40; version++) { for (int version = 1; version <= 40; version++) {
int dimension = 17 + 4 * version; int dimension = 17 + 4 * version;
testMask(mask, dimension, condition); testMask(mask, dimension, condition);

View file

@ -192,7 +192,7 @@ public final class MaskUtilTestCase extends Assert {
{1, 0, 1, 0, 1, 0}, {1, 0, 1, 0, 1, 0},
{0, 1, 0, 1, 0, 1}, {0, 1, 0, 1, 0, 1},
}; };
assertTrue(TestGetDataMaskBitInternal(0, mask0)); assertTrue(TestGetDataMaskBitInternal(0, mask0));
int[][] mask1 = { int[][] mask1 = {
{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
@ -201,7 +201,7 @@ public final class MaskUtilTestCase extends Assert {
{1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
}; };
assertTrue(TestGetDataMaskBitInternal(1, mask1)); assertTrue(TestGetDataMaskBitInternal(1, mask1));
int[][] mask2 = { int[][] mask2 = {
{1, 0, 0, 1, 0, 0}, {1, 0, 0, 1, 0, 0},
{1, 0, 0, 1, 0, 0}, {1, 0, 0, 1, 0, 0},