mirror of
https://github.com/zxing/zxing.git
synced 2024-11-09 20:44:03 -08:00
More javadoc
git-svn-id: https://zxing.googlecode.com/svn/trunk@13 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
cc66a80d3e
commit
d6cfb083a1
|
@ -29,8 +29,8 @@ final class BitMatrixParser {
|
|||
private FormatInformation parsedFormatInfo;
|
||||
|
||||
/**
|
||||
* @throws com.google.zxing.ReaderException
|
||||
* if dimension is not >= 21 and 1 mod 4
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
* @throws ReaderException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
BitMatrixParser(BitMatrix bitMatrix) throws ReaderException {
|
||||
int dimension = bitMatrix.getDimension();
|
||||
|
@ -40,6 +40,13 @@ final class BitMatrixParser {
|
|||
this.bitMatrix = bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws ReaderException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
FormatInformation readFormatInformation() throws ReaderException {
|
||||
|
||||
if (parsedFormatInfo != null) {
|
||||
|
@ -83,6 +90,13 @@ final class BitMatrixParser {
|
|||
throw new ReaderException("Could not decode format information");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws ReaderException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
Version readVersion() throws ReaderException {
|
||||
|
||||
if (parsedVersion != null) {
|
||||
|
@ -130,11 +144,21 @@ final class BitMatrixParser {
|
|||
return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstitute the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws ReaderException if the exact number of bytes expected is not read
|
||||
*/
|
||||
byte[] readCodewords() throws ReaderException {
|
||||
|
||||
FormatInformation formatInfo = readFormatInformation();
|
||||
Version version = readVersion();
|
||||
|
||||
// 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.
|
||||
DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
|
||||
int dimension = bitMatrix.getDimension();
|
||||
dataMask.unmaskBitMatrix(bitMatrix.getBits(), dimension);
|
||||
|
@ -146,21 +170,26 @@ final class BitMatrixParser {
|
|||
int resultOffset = 0;
|
||||
int currentByte = 0;
|
||||
int bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for (int j = dimension - 1; j > 0; j -= 2) {
|
||||
if (j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for (int count = 0; count < dimension; count++) {
|
||||
int i = readingUp ? dimension - 1 - count : count;
|
||||
for (int col = 0; col < 2; col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!functionPattern.get(i, j - col)) {
|
||||
// Read a bit
|
||||
bitsRead++;
|
||||
currentByte <<= 1;
|
||||
if (bitMatrix.get(i, j - col)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if (bitsRead == 8) {
|
||||
result[resultOffset++] = (byte) currentByte;
|
||||
bitsRead = 0;
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
|
||||
* number of bits read is not often a multiple of 8.
|
||||
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
|
||||
* number of bits read is not often a multiple of 8.</p>
|
||||
*
|
||||
* <p>This class is not thread-safe.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
|
@ -28,11 +30,18 @@ final class BitSource {
|
|||
private int byteOffset;
|
||||
private int bitOffset;
|
||||
|
||||
/**
|
||||
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
|
||||
* Bits are read within a byte from most-significant to least-significant bit.
|
||||
*/
|
||||
BitSource(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numBits number of bits to read
|
||||
* @return int representing the bits read. The bits will appear as the least-significant
|
||||
* bits of the int
|
||||
* @throws IllegalArgumentException if numBits isn't in [1,32]
|
||||
*/
|
||||
int readBits(int numBits) {
|
||||
|
@ -77,6 +86,9 @@ final class BitSource {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
int available() {
|
||||
return 8 * (bytes.length - byteOffset) - bitOffset;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
final class DataBlock {
|
||||
|
@ -29,15 +33,32 @@ final class DataBlock {
|
|||
this.codewords = codewords;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>When QR Codes use multiple data blocks, they are actually interleave the bytes of each of them.
|
||||
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
* method will separate the data into original blocks.</p>
|
||||
*
|
||||
* @param rawCodewords bytes as read directly from the QR Code
|
||||
* @param version version of the QR Code
|
||||
* @param ecLevel error-correction level of the QR Code
|
||||
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
static DataBlock[] getDataBlocks(byte[] rawCodewords,
|
||||
Version version,
|
||||
ErrorCorrectionLevel ecLevel) {
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
|
||||
|
||||
// First count the total number of data blocks
|
||||
int totalBlocks = 0;
|
||||
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||
for (int i = 0; i < ecBlockArray.length; i++) {
|
||||
totalBlocks += ecBlockArray[i].getCount();
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
DataBlock[] result = new DataBlock[totalBlocks];
|
||||
int numResultBlocks = 0;
|
||||
for (int j = 0; j < ecBlockArray.length; j++) {
|
||||
|
@ -45,8 +66,7 @@ final class DataBlock {
|
|||
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||
int numDataCodewords = ecBlock.getDataCodewords();
|
||||
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
||||
result[numResultBlocks++] =
|
||||
new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,21 +75,18 @@ final class DataBlock {
|
|||
int shorterBlocksTotalCodewords = result[0].codewords.length;
|
||||
int longerBlocksStartAt = result.length - 1;
|
||||
while (longerBlocksStartAt >= 0) {
|
||||
int numCodewords =
|
||||
result[longerBlocksStartAt].codewords.length;
|
||||
int numCodewords = result[longerBlocksStartAt].codewords.length;
|
||||
if (numCodewords == shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
if (numCodewords != shorterBlocksTotalCodewords + 1) {
|
||||
throw new IllegalStateException(
|
||||
"Data block sizes differ by more than 1");
|
||||
throw new IllegalStateException("Data block sizes differ by more than 1");
|
||||
}
|
||||
longerBlocksStartAt--;
|
||||
}
|
||||
longerBlocksStartAt++;
|
||||
|
||||
int shorterBlocksNumDataCodewords =
|
||||
shorterBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
int rawCodewordsOffset = 0;
|
||||
|
@ -80,8 +97,7 @@ final class DataBlock {
|
|||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
|
||||
result[j].codewords[shorterBlocksNumDataCodewords] =
|
||||
rawCodewords[rawCodewordsOffset++];
|
||||
result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
int max = result[0].codewords.length;
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
* <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
* of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
|
||||
* including areas used for finder patterns, timing patterns, etc. These areas should be unused
|
||||
* after the point they are unmasked anyway.
|
||||
* after the point they are unmasked anyway.</p>
|
||||
*
|
||||
* Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
* and j is row position. In fact, as the text says, i is row position and j is column position.
|
||||
* <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
* and j is row position. In fact, as the text says, i is row position and j is column position.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
|
@ -46,8 +46,20 @@ abstract class DataMask {
|
|||
private DataMask() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param bits representation of QR Code bits from {@link com.google.zxing.common.BitMatrix#getBits()}
|
||||
* @param dimension dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
abstract void unmaskBitMatrix(int[] bits, int dimension);
|
||||
|
||||
/**
|
||||
* @param reference a value between 0 and 7 indicating one of the eight possible
|
||||
* data mask patterns a QR Code may use
|
||||
* @return {@link DataMask} encapsulating the data mask pattern
|
||||
*/
|
||||
static DataMask forReference(int reference) {
|
||||
if (reference < 0 || reference > 7) {
|
||||
throw new IllegalArgumentException();
|
||||
|
|
|
@ -21,7 +21,10 @@ import com.google.zxing.ReaderException;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.3 - 6.4.7
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
* in one QR Code. This class decodes the bits back into text.</p>
|
||||
*
|
||||
* <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
|
@ -54,8 +57,9 @@ final class DecodedBitStreamParser {
|
|||
Mode mode;
|
||||
do {
|
||||
// While still another segment to read...
|
||||
mode = Mode.forBits(bits.readBits(4));
|
||||
mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
||||
if (!mode.equals(Mode.TERMINATOR)) {
|
||||
// How many characters will follow, encoded in this mode?
|
||||
int count = bits.readBits(mode.getCharacterCountBits(version));
|
||||
if (mode.equals(Mode.NUMERIC)) {
|
||||
decodeNumericSegment(bits, result, count);
|
||||
|
@ -66,11 +70,12 @@ final class DecodedBitStreamParser {
|
|||
} else if (mode.equals(Mode.KANJI)) {
|
||||
decodeKanjiSegment(bits, result, count);
|
||||
} else {
|
||||
throw new ReaderException("Unsupported mode indicator: " + mode);
|
||||
throw new ReaderException("Unsupported mode indicator");
|
||||
}
|
||||
}
|
||||
} while (!mode.equals(Mode.TERMINATOR));
|
||||
|
||||
// I thought it wasn't allowed to leave extra bytes after the terminator but it happens
|
||||
/*
|
||||
int bitsLeft = bits.available();
|
||||
if (bitsLeft > 0) {
|
||||
|
@ -85,9 +90,12 @@ final class DecodedBitStreamParser {
|
|||
private static void decodeKanjiSegment(BitSource bits,
|
||||
StringBuffer result,
|
||||
int count) throws ReaderException {
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
byte[] buffer = new byte[2 * count];
|
||||
int offset = 0;
|
||||
while (count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
int twoBytes = bits.readBits(13);
|
||||
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||
if (assembledTwoBytes < 0x01F00) {
|
||||
|
@ -144,7 +152,7 @@ final class DecodedBitStreamParser {
|
|||
count -= 2;
|
||||
}
|
||||
if (count == 1) {
|
||||
// special case on char left
|
||||
// special case: one character left
|
||||
result.append(ALPHANUMERIC_CHARS[bits.readBits(6)]);
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +160,9 @@ final class DecodedBitStreamParser {
|
|||
private static void decodeNumericSegment(BitSource bits,
|
||||
StringBuffer result,
|
||||
int count) throws ReaderException {
|
||||
// Read three digits at a time
|
||||
while (count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
int threeDigitsBits = bits.readBits(10);
|
||||
if (threeDigitsBits >= 1000) {
|
||||
throw new ReaderException("Illegal value for 3-digit unit: " + threeDigitsBits);
|
||||
|
@ -163,6 +173,7 @@ final class DecodedBitStreamParser {
|
|||
count -= 3;
|
||||
}
|
||||
if (count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
int twoDigitsBits = bits.readBits(7);
|
||||
if (twoDigitsBits >= 100) {
|
||||
throw new ReaderException("Illegal value for 2-digit unit: " + twoDigitsBits);
|
||||
|
@ -170,6 +181,7 @@ final class DecodedBitStreamParser {
|
|||
result.append(ALPHANUMERIC_CHARS[twoDigitsBits / 10]);
|
||||
result.append(ALPHANUMERIC_CHARS[twoDigitsBits % 10]);
|
||||
} else if (count == 1) {
|
||||
// One digit left over to read
|
||||
int digitBits = bits.readBits(4);
|
||||
if (digitBits >= 10) {
|
||||
throw new ReaderException("Illegal value for digit unit: " + digitBits);
|
||||
|
|
|
@ -22,6 +22,9 @@ import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
|||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
* the QR Code from an image.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
*/
|
||||
public final class Decoder {
|
||||
|
@ -29,6 +32,14 @@ public final class Decoder {
|
|||
private Decoder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param image booleans representing white/black QR Code modules
|
||||
* @return text encoded within the QR Code
|
||||
* @throws ReaderException if the QR Code cannot be decoded
|
||||
*/
|
||||
public static String decode(boolean[][] image) throws ReaderException {
|
||||
int dimension = image.length;
|
||||
BitMatrix bits = new BitMatrix(dimension);
|
||||
|
@ -42,18 +53,34 @@ public final class Decoder {
|
|||
return decode(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param bits booleans representing white/black QR Code modules
|
||||
* @return text encoded within the QR Code
|
||||
* @throws ReaderException if the QR Code cannot be decoded
|
||||
*/
|
||||
public static String decode(BitMatrix bits) throws ReaderException {
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||
Version version = parser.readVersion();
|
||||
ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel();
|
||||
|
||||
// Read codewords
|
||||
byte[] codewords = parser.readCodewords();
|
||||
// Separate into data blocks
|
||||
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel);
|
||||
|
||||
// Count total number of data bytes
|
||||
int totalBytes = 0;
|
||||
for (int i = 0; i < dataBlocks.length; i++) {
|
||||
totalBytes += dataBlocks[i].getNumDataCodewords();
|
||||
}
|
||||
byte[] resultBytes = new byte[totalBytes];
|
||||
int resultOffset = 0;
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
for (int j = 0; j < dataBlocks.length; j++) {
|
||||
DataBlock dataBlock = dataBlocks[j];
|
||||
byte[] codewordBytes = dataBlock.getCodewords();
|
||||
|
@ -64,12 +91,21 @@ public final class Decoder {
|
|||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser.decode(resultBytes, version);
|
||||
}
|
||||
|
||||
private static void correctErrors(byte[] codewordBytes, int numDataCodewords)
|
||||
throws ReaderException {
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
* @throws ReaderException if error correction fails
|
||||
*/
|
||||
private static void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ReaderException {
|
||||
int numCodewords = codewordBytes.length;
|
||||
// First read into an array of ints
|
||||
int[] codewordsInts = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++) {
|
||||
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||
|
@ -80,6 +116,8 @@ public final class Decoder {
|
|||
} catch (ReedSolomonException rse) {
|
||||
throw new ReaderException(rse.toString());
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
codewordBytes[i] = (byte) codewordsInts[i];
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ final class ErrorCorrectionLevel {
|
|||
|
||||
private final int ordinal;
|
||||
|
||||
private ErrorCorrectionLevel(final int ordinal) {
|
||||
private ErrorCorrectionLevel(int ordinal) {
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,10 @@ final class ErrorCorrectionLevel {
|
|||
return ordinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits int containing the two bits encoding a QR Code's error correction level
|
||||
* @return {@link ErrorCorrectionLevel} representing the encoded error correction level
|
||||
*/
|
||||
static ErrorCorrectionLevel forBits(int bits) {
|
||||
return FOR_BITS[bits];
|
||||
}
|
||||
|
|
|
@ -17,7 +17,12 @@
|
|||
package com.google.zxing.qrcode.decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
* error correction level.</p>
|
||||
*
|
||||
* @author srowen@google.com (Sean Owen)
|
||||
* @see DataMask
|
||||
* @see ErrorCorrectionLevel
|
||||
*/
|
||||
final class FormatInformation {
|
||||
|
||||
|
@ -61,6 +66,7 @@ final class FormatInformation {
|
|||
{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 =
|
||||
new int[]{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
||||
|
||||
|
@ -69,16 +75,15 @@ final class FormatInformation {
|
|||
|
||||
private FormatInformation(int formatInfo) {
|
||||
// Bits 3,4
|
||||
errorCorrectionLevel =
|
||||
ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
|
||||
errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
dataMask = (byte) (formatInfo & 0x07);
|
||||
}
|
||||
|
||||
static int numBitsDiffering(int a, int b) {
|
||||
a ^= b;
|
||||
return
|
||||
BITS_SET_IN_HALF_BYTE[a & 0x0F] +
|
||||
a ^= b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// 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)] +
|
||||
|
@ -88,6 +93,11 @@ final class FormatInformation {
|
|||
BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rawFormatInfo
|
||||
* @return
|
||||
*/
|
||||
static FormatInformation decodeFormatInformation(int rawFormatInfo) {
|
||||
FormatInformation formatInfo = doDecodeFormatInformation(rawFormatInfo);
|
||||
if (formatInfo != null) {
|
||||
|
@ -99,16 +109,17 @@ final class FormatInformation {
|
|||
return doDecodeFormatInformation(rawFormatInfo ^ FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
|
||||
private static FormatInformation doDecodeFormatInformation(
|
||||
int rawFormatInfo) {
|
||||
private static FormatInformation doDecodeFormatInformation(int rawFormatInfo) {
|
||||
// Unmask:
|
||||
int unmaskedFormatInfo = rawFormatInfo ^ FORMAT_INFO_MASK_QR;
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
int bestDifference = Integer.MAX_VALUE;
|
||||
int bestFormatInfo = 0;
|
||||
for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
|
||||
int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
|
||||
int targetInfo = decodeInfo[0];
|
||||
if (targetInfo == unmaskedFormatInfo) {
|
||||
// Found an exact match
|
||||
return new FormatInformation(decodeInfo[1]);
|
||||
}
|
||||
int bitsDifference = numBitsDiffering(unmaskedFormatInfo, targetInfo);
|
||||
|
|
|
@ -40,6 +40,11 @@ final class Mode {
|
|||
this.characterCountBitsForVersions = characterCountBitsForVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits four bits encoding a QR Code data mode
|
||||
* @return {@link Mode} encoded by these bits
|
||||
* @throws ReaderException if bits do not correspond to a known mode
|
||||
*/
|
||||
static Mode forBits(int bits) throws ReaderException {
|
||||
switch (bits) {
|
||||
case 0x0:
|
||||
|
@ -57,6 +62,11 @@ final class Mode {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this {@link Mode}
|
||||
*/
|
||||
int getCharacterCountBits(Version version) {
|
||||
int number = version.getVersionNumber();
|
||||
int offset;
|
||||
|
|
|
@ -87,41 +87,49 @@ public final class Version {
|
|||
return ecBlocks[ecLevel.ordinal()];
|
||||
}
|
||||
|
||||
public static Version getProvisionalVersionForDimension(int dimension)
|
||||
throws ReaderException {
|
||||
/**
|
||||
* <p>Deduces version information purely from QR Code dimensions.</p>
|
||||
*
|
||||
* @param dimension dimension in modules
|
||||
* @return {@link Version} for a QR Code of that dimension
|
||||
* @throws ReaderException if dimension is not 1 mod 4
|
||||
*/
|
||||
public static Version getProvisionalVersionForDimension(int dimension) throws ReaderException {
|
||||
if (dimension % 4 != 1) {
|
||||
throw new ReaderException("Dimension must be 1 mod 4");
|
||||
}
|
||||
return getVersionForNumber((dimension - 17) >> 2);
|
||||
}
|
||||
|
||||
public static Version getVersionForNumber(int versionNumber)
|
||||
throws ReaderException {
|
||||
public static Version getVersionForNumber(int versionNumber) throws ReaderException {
|
||||
if (versionNumber < 1 || versionNumber > 40) {
|
||||
throw new ReaderException(
|
||||
"versionNumber must be between 1 and 40");
|
||||
throw new ReaderException("versionNumber must be between 1 and 40");
|
||||
}
|
||||
return VERSIONS[versionNumber - 1];
|
||||
}
|
||||
|
||||
static Version decodeVersionInformation(int versionBits)
|
||||
throws ReaderException {
|
||||
static Version decodeVersionInformation(int versionBits) throws ReaderException {
|
||||
int bestDifference = Integer.MAX_VALUE;
|
||||
int bestVersion = 0;
|
||||
for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
|
||||
int targetVersion = VERSION_DECODE_INFO[i];
|
||||
// Do the version info bits match exactly? done.
|
||||
if (targetVersion == versionBits) {
|
||||
return getVersionForNumber(i + 7);
|
||||
}
|
||||
int bitsDifference =
|
||||
FormatInformation.numBitsDiffering(versionBits, targetVersion);
|
||||
// Otherwise see if this is the closest to a real version info bit string
|
||||
// we have seen so far
|
||||
int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
|
||||
if (bitsDifference < bestDifference) {
|
||||
bestVersion = i + 7;
|
||||
}
|
||||
}
|
||||
// We can tolerate up to 3 bits of error since no two version info codewords will
|
||||
// differ in less than 4 bits.
|
||||
if (bestDifference <= 3) {
|
||||
return getVersionForNumber(bestVersion);
|
||||
}
|
||||
// If we didn't find a close enough match, fail
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue