aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/zxing/src/com/google/zxing/qrcode
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/zxing/src/com/google/zxing/qrcode')
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java108
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java203
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java123
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java155
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java322
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java86
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java171
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java117
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java586
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java48
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java279
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java406
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java63
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java585
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java49
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java37
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java97
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java557
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java218
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java524
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java239
21 files changed, 4973 insertions, 0 deletions
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
new file mode 100644
index 000000000..fff4f5d1e
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.Writer;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.encoder.ByteMatrix;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.encoder.Encoder;
+import com.google.zxing.qrcode.encoder.QRCode;
+
+import java.util.Hashtable;
+
+/**
+ * This object renders a QR Code as a BitMatrix 2D array of greyscale values.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class QRCodeWriter implements Writer {
+
+ private static final int QUIET_ZONE_SIZE = 0; // patched for Bitcoin Wallet
+
+ public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
+ throws WriterException {
+
+ return encode(contents, format, width, height, null);
+ }
+
+ public BitMatrix encode(String contents, BarcodeFormat format, int width, int height,
+ Hashtable hints) throws WriterException {
+
+ if (contents == null || contents.length() == 0) {
+ throw new IllegalArgumentException("Found empty contents");
+ }
+
+ if (format != BarcodeFormat.QR_CODE) {
+ throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
+ }
+
+ if (width < 0 || height < 0) {
+ throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
+ height);
+ }
+
+ ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
+ if (hints != null) {
+ ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
+ if (requestedECLevel != null) {
+ errorCorrectionLevel = requestedECLevel;
+ }
+ }
+
+ QRCode code = new QRCode();
+ Encoder.encode(contents, errorCorrectionLevel, hints, code);
+ return renderResult(code, width, height);
+ }
+
+ // Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
+ // 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
+ private static BitMatrix renderResult(QRCode code, int width, int height) {
+ ByteMatrix input = code.getMatrix();
+ int inputWidth = input.getWidth();
+ int inputHeight = input.getHeight();
+ int qrWidth = inputWidth + (QUIET_ZONE_SIZE << 1);
+ int qrHeight = inputHeight + (QUIET_ZONE_SIZE << 1);
+ int outputWidth = Math.max(width, qrWidth);
+ int outputHeight = Math.max(height, qrHeight);
+
+ int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
+ // Padding includes both the quiet zone and the extra white pixels to accommodate the requested
+ // dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
+ // If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
+ // handle all the padding from 100x100 (the actual QR) up to 200x160.
+ int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
+ int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
+
+ BitMatrix output = new BitMatrix(outputWidth, outputHeight);
+
+ for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
+ // Write the contents of this row of the barcode
+ for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
+ if (input.get(inputX, inputY) == 1) {
+ output.setRegion(outputX, outputY, multiple, multiple);
+ }
+ }
+ }
+
+ return output;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
new file mode 100644
index 000000000..9d131a554
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * @author Sean Owen
+ */
+final class BitMatrixParser {
+
+ private final BitMatrix bitMatrix;
+ private Version parsedVersion;
+ private FormatInformation parsedFormatInfo;
+
+ /**
+ * @param bitMatrix {@link BitMatrix} to parse
+ * @throws FormatException if dimension is not >= 21 and 1 mod 4
+ */
+ BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
+ int dimension = bitMatrix.getHeight();
+ if (dimension < 21 || (dimension & 0x03) != 1) {
+ throw FormatException.getFormatInstance();
+ }
+ 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 FormatException if both format information locations cannot be parsed as
+ * the valid encoding of format information
+ */
+ FormatInformation readFormatInformation() throws FormatException {
+
+ if (parsedFormatInfo != null) {
+ return parsedFormatInfo;
+ }
+
+ // Read top-left format info bits
+ int formatInfoBits1 = 0;
+ for (int i = 0; i < 6; i++) {
+ formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
+ }
+ // .. and skip a bit in the timing pattern ...
+ formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
+ formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
+ formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
+ // .. and skip a bit in the timing pattern ...
+ for (int j = 5; j >= 0; j--) {
+ formatInfoBits1 = copyBit(8, j, formatInfoBits1);
+ }
+
+ // Read the top-right/bottom-left pattern too
+ int dimension = bitMatrix.getHeight();
+ int formatInfoBits2 = 0;
+ int jMin = dimension - 7;
+ for (int j = dimension - 1; j >= jMin; j--) {
+ formatInfoBits2 = copyBit(8, j, formatInfoBits2);
+ }
+ for (int i = dimension - 8; i < dimension; i++) {
+ formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
+ }
+
+ parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
+ if (parsedFormatInfo != null) {
+ return parsedFormatInfo;
+ }
+ throw FormatException.getFormatInstance();
+ }
+
+ /**
+ * <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 FormatException if both version information locations cannot be parsed as
+ * the valid encoding of version information
+ */
+ Version readVersion() throws FormatException {
+
+ if (parsedVersion != null) {
+ return parsedVersion;
+ }
+
+ int dimension = bitMatrix.getHeight();
+
+ int provisionalVersion = (dimension - 17) >> 2;
+ if (provisionalVersion <= 6) {
+ return Version.getVersionForNumber(provisionalVersion);
+ }
+
+ // Read top-right version info: 3 wide by 6 tall
+ int versionBits = 0;
+ int ijMin = dimension - 11;
+ for (int j = 5; j >= 0; j--) {
+ for (int i = dimension - 9; i >= ijMin; i--) {
+ versionBits = copyBit(i, j, versionBits);
+ }
+ }
+
+ parsedVersion = Version.decodeVersionInformation(versionBits);
+ if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
+ return parsedVersion;
+ }
+
+ // Hmm, failed. Try bottom left: 6 wide by 3 tall
+ versionBits = 0;
+ for (int i = 5; i >= 0; i--) {
+ for (int j = dimension - 9; j >= ijMin; j--) {
+ versionBits = copyBit(i, j, versionBits);
+ }
+ }
+
+ parsedVersion = Version.decodeVersionInformation(versionBits);
+ if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
+ return parsedVersion;
+ }
+ throw FormatException.getFormatInstance();
+ }
+
+ private int copyBit(int i, int j, int versionBits) {
+ 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 FormatException if the exact number of bytes expected is not read
+ */
+ byte[] readCodewords() throws FormatException {
+
+ 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.getHeight();
+ dataMask.unmaskBitMatrix(bitMatrix, dimension);
+
+ BitMatrix functionPattern = version.buildFunctionPattern();
+
+ boolean readingUp = true;
+ byte[] result = new byte[version.getTotalCodewords()];
+ 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(j - col, i)) {
+ // Read a bit
+ bitsRead++;
+ currentByte <<= 1;
+ if (bitMatrix.get(j - col, i)) {
+ currentByte |= 1;
+ }
+ // If we've made a whole byte, save it off
+ if (bitsRead == 8) {
+ result[resultOffset++] = (byte) currentByte;
+ bitsRead = 0;
+ currentByte = 0;
+ }
+ }
+ }
+ }
+ readingUp ^= true; // readingUp = !readingUp; // switch directions
+ }
+ if (resultOffset != version.getTotalCodewords()) {
+ throw FormatException.getFormatInstance();
+ }
+ return result;
+ }
+
+} \ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
new file mode 100644
index 000000000..12959d9c1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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 Sean Owen
+ */
+final class DataBlock {
+
+ private final int numDataCodewords;
+ private final byte[] codewords;
+
+ private DataBlock(int numDataCodewords, byte[] codewords) {
+ this.numDataCodewords = numDataCodewords;
+ this.codewords = codewords;
+ }
+
+ /**
+ * <p>When QR Codes use multiple data blocks, they are actually interleaved.
+ * 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 DataBlocks containing original bytes, "de-interleaved" from representation in the
+ * QR Code
+ */
+ static DataBlock[] getDataBlocks(byte[] rawCodewords,
+ Version version,
+ ErrorCorrectionLevel ecLevel) {
+
+ if (rawCodewords.length != version.getTotalCodewords()) {
+ throw new IllegalArgumentException();
+ }
+
+ // 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++) {
+ Version.ECB ecBlock = ecBlockArray[j];
+ for (int i = 0; i < ecBlock.getCount(); i++) {
+ int numDataCodewords = ecBlock.getDataCodewords();
+ int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;
+ result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
+ }
+ }
+
+ // All blocks have the same amount of data, except that the last n
+ // (where n may be 0) have 1 more byte. Figure out where these start.
+ int shorterBlocksTotalCodewords = result[0].codewords.length;
+ int longerBlocksStartAt = result.length - 1;
+ while (longerBlocksStartAt >= 0) {
+ int numCodewords = result[longerBlocksStartAt].codewords.length;
+ if (numCodewords == shorterBlocksTotalCodewords) {
+ break;
+ }
+ longerBlocksStartAt--;
+ }
+ longerBlocksStartAt++;
+
+ int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();
+ // The last elements of result may be 1 element longer;
+ // first fill out as many elements as all of them have
+ int rawCodewordsOffset = 0;
+ for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
+ for (int j = 0; j < numResultBlocks; j++) {
+ result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
+ }
+ }
+ // Fill out the last data block in the longer ones
+ for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
+ result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
+ }
+ // Now add in error correction blocks
+ int max = result[0].codewords.length;
+ for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
+ for (int j = 0; j < numResultBlocks; j++) {
+ int iOffset = j < longerBlocksStartAt ? i : i + 1;
+ result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
+ }
+ }
+ return result;
+ }
+
+ int getNumDataCodewords() {
+ return numDataCodewords;
+ }
+
+ byte[] getCodewords() {
+ return codewords;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
new file mode 100644
index 000000000..d29dbd47f
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * <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.</p>
+ *
+ * <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 Sean Owen
+ */
+abstract class DataMask {
+
+ /**
+ * See ISO 18004:2006 6.8.1
+ */
+ private static final DataMask[] DATA_MASKS = {
+ new DataMask000(),
+ new DataMask001(),
+ new DataMask010(),
+ new DataMask011(),
+ new DataMask100(),
+ new DataMask101(),
+ new DataMask110(),
+ new DataMask111(),
+ };
+
+ 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
+ * @param dimension dimension of QR Code, represented by bits, being unmasked
+ */
+ final void unmaskBitMatrix(BitMatrix bits, int dimension) {
+ for (int i = 0; i < dimension; i++) {
+ for (int j = 0; j < dimension; j++) {
+ if (isMasked(i, j)) {
+ bits.flip(j, i);
+ }
+ }
+ }
+ }
+
+ 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 class DataMask000 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return ((i + j) & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 001: mask bits for which x mod 2 == 0
+ */
+ private static class DataMask001 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return (i & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 010: mask bits for which y mod 3 == 0
+ */
+ private static class DataMask010 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return j % 3 == 0;
+ }
+ }
+
+ /**
+ * 011: mask bits for which (x + y) mod 3 == 0
+ */
+ private static class DataMask011 extends DataMask {
+ 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 class DataMask100 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return (((i >>> 1) + (j /3)) & 0x01) == 0;
+ }
+ }
+
+ /**
+ * 101: mask bits for which xy mod 2 + xy mod 3 == 0
+ */
+ private static class DataMask101 extends DataMask {
+ 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 class DataMask110 extends DataMask {
+ 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 class DataMask111 extends DataMask {
+ boolean isMasked(int i, int j) {
+ return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
+ }
+ }
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
new file mode 100644
index 000000000..ff374ac50
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitSource;
+import com.google.zxing.common.CharacterSetECI;
+import com.google.zxing.common.DecoderResult;
+import com.google.zxing.common.StringUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * <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 Sean Owen
+ */
+final class DecodedBitStreamParser {
+
+ /**
+ * See ISO 18004:2006, 6.4.4 Table 5
+ */
+ private static final char[] ALPHANUMERIC_CHARS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+ '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 DecodedBitStreamParser() {
+ }
+
+ static DecoderResult decode(byte[] bytes, Version version, ErrorCorrectionLevel ecLevel, Hashtable hints)
+ throws FormatException {
+ BitSource bits = new BitSource(bytes);
+ StringBuffer result = new StringBuffer(50);
+ CharacterSetECI currentCharacterSetECI = null;
+ boolean fc1InEffect = false;
+ Vector byteSegments = new Vector(1);
+ Mode mode;
+ do {
+ // While still another segment to read...
+ if (bits.available() < 4) {
+ // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
+ mode = Mode.TERMINATOR;
+ } else {
+ try {
+ mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
+ } catch (IllegalArgumentException iae) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+ if (!mode.equals(Mode.TERMINATOR)) {
+ if (mode.equals(Mode.FNC1_FIRST_POSITION) || mode.equals(Mode.FNC1_SECOND_POSITION)) {
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ fc1InEffect = true;
+ } else if (mode.equals(Mode.STRUCTURED_APPEND)) {
+ // not really supported; all we do is ignore it
+ // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
+ bits.readBits(16);
+ } else if (mode.equals(Mode.ECI)) {
+ // Count doesn't apply to ECI
+ int value = parseECIValue(bits);
+ currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
+ if (currentCharacterSetECI == null) {
+ throw FormatException.getFormatInstance();
+ }
+ } else {
+ // First handle Hanzi mode which does not start with character count
+ if (mode.equals(Mode.HANZI)) {
+ //chinese mode contains a sub set indicator right after mode indicator
+ int subset = bits.readBits(4);
+ int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
+ if (subset == GB2312_SUBSET) {
+ decodeHanziSegment(bits, result, countHanzi);
+ }
+ } else {
+ // "Normal" QR code modes:
+ // 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);
+ } else if (mode.equals(Mode.ALPHANUMERIC)) {
+ decodeAlphanumericSegment(bits, result, count, fc1InEffect);
+ } else if (mode.equals(Mode.BYTE)) {
+ decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
+ } else if (mode.equals(Mode.KANJI)) {
+ decodeKanjiSegment(bits, result, count);
+ } else {
+ throw FormatException.getFormatInstance();
+ }
+ }
+ }
+ }
+ } while (!mode.equals(Mode.TERMINATOR));
+
+ return new DecoderResult(bytes,
+ result.toString(),
+ byteSegments.isEmpty() ? null : byteSegments,
+ ecLevel == null ? null : ecLevel.toString());
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ */
+ private static void decodeHanziSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as GB2312 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 / 0x060) << 8) | (twoBytes % 0x060);
+ if (assembledTwoBytes < 0x003BF) {
+ // In the 0xA1A1 to 0xAAFE range
+ assembledTwoBytes += 0x0A1A1;
+ } else {
+ // In the 0xB0A1 to 0xFAFE range
+ assembledTwoBytes += 0x0A6A1;
+ }
+ buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);
+ buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);
+ offset += 2;
+ count--;
+ }
+
+ try {
+ result.append(new String(buffer, StringUtils.GB2312));
+ } catch (UnsupportedEncodingException uee) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ private static void decodeKanjiSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ // 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) {
+ // In the 0x8140 to 0x9FFC range
+ assembledTwoBytes += 0x08140;
+ } else {
+ // In the 0xE040 to 0xEBBF range
+ assembledTwoBytes += 0x0C140;
+ }
+ buffer[offset] = (byte) (assembledTwoBytes >> 8);
+ buffer[offset + 1] = (byte) assembledTwoBytes;
+ offset += 2;
+ count--;
+ }
+ // Shift_JIS may not be supported in some environments:
+ try {
+ result.append(new String(buffer, StringUtils.SHIFT_JIS));
+ } catch (UnsupportedEncodingException uee) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ private static void decodeByteSegment(BitSource bits,
+ StringBuffer result,
+ int count,
+ CharacterSetECI currentCharacterSetECI,
+ Vector byteSegments,
+ Hashtable hints) throws FormatException {
+ // Don't crash trying to read more bits than we have available.
+ if (count << 3 > bits.available()) {
+ throw FormatException.getFormatInstance();
+ }
+
+ byte[] readBytes = new byte[count];
+ for (int i = 0; i < count; i++) {
+ readBytes[i] = (byte) bits.readBits(8);
+ }
+ String encoding;
+ if (currentCharacterSetECI == null) {
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: t does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+ encoding = StringUtils.guessEncoding(readBytes, hints);
+ } else {
+ encoding = currentCharacterSetECI.getEncodingName();
+ }
+ try {
+ result.append(new String(readBytes, encoding));
+ } catch (UnsupportedEncodingException uce) {
+ throw FormatException.getFormatInstance();
+ }
+ byteSegments.addElement(readBytes);
+ }
+
+ private static char toAlphaNumericChar(int value) throws FormatException {
+ if (value >= ALPHANUMERIC_CHARS.length) {
+ throw FormatException.getFormatInstance();
+ }
+ return ALPHANUMERIC_CHARS[value];
+ }
+
+ private static void decodeAlphanumericSegment(BitSource bits,
+ StringBuffer result,
+ int count,
+ boolean fc1InEffect) throws FormatException {
+ // Read two characters at a time
+ int start = result.length();
+ while (count > 1) {
+ int nextTwoCharsBits = bits.readBits(11);
+ result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
+ result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
+ count -= 2;
+ }
+ if (count == 1) {
+ // special case: one character left
+ result.append(toAlphaNumericChar(bits.readBits(6)));
+ }
+ // See section 6.4.8.1, 6.4.8.2
+ if (fc1InEffect) {
+ // We need to massage the result a bit if in an FNC1 mode:
+ for (int i = start; i < result.length(); i++) {
+ if (result.charAt(i) == '%') {
+ if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
+ // %% is rendered as %
+ result.deleteCharAt(i + 1);
+ } else {
+ // In alpha mode, % should be converted to FNC1 separator 0x1D
+ result.setCharAt(i, (char) 0x1D);
+ }
+ }
+ }
+ }
+ }
+
+ private static void decodeNumericSegment(BitSource bits,
+ StringBuffer result,
+ int count) throws FormatException {
+ // Read three digits at a time
+ while (count >= 3) {
+ // Each 10 bits encodes three digits
+ int threeDigitsBits = bits.readBits(10);
+ if (threeDigitsBits >= 1000) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(threeDigitsBits / 100));
+ result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
+ result.append(toAlphaNumericChar(threeDigitsBits % 10));
+ count -= 3;
+ }
+ if (count == 2) {
+ // Two digits left over to read, encoded in 7 bits
+ int twoDigitsBits = bits.readBits(7);
+ if (twoDigitsBits >= 100) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(twoDigitsBits / 10));
+ result.append(toAlphaNumericChar(twoDigitsBits % 10));
+ } else if (count == 1) {
+ // One digit left over to read
+ int digitBits = bits.readBits(4);
+ if (digitBits >= 10) {
+ throw FormatException.getFormatInstance();
+ }
+ result.append(toAlphaNumericChar(digitBits));
+ }
+ }
+
+ private static int parseECIValue(BitSource bits) {
+ int firstByte = bits.readBits(8);
+ if ((firstByte & 0x80) == 0) {
+ // just one byte
+ return firstByte & 0x7F;
+ } else if ((firstByte & 0xC0) == 0x80) {
+ // two bytes
+ int secondByte = bits.readBits(8);
+ return ((firstByte & 0x3F) << 8) | secondByte;
+ } else if ((firstByte & 0xE0) == 0xC0) {
+ // three bytes
+ int secondThirdBytes = bits.readBits(16);
+ return ((firstByte & 0x1F) << 16) | secondThirdBytes;
+ }
+ throw new IllegalArgumentException("Bad ECI bits starting with byte " + firstByte);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
new file mode 100644
index 000000000..e8d6c2589
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+/**
+ * <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
+ * defined by the QR code standard.</p>
+ *
+ * @author Sean Owen
+ */
+public final class ErrorCorrectionLevel {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ /**
+ * L = ~7% correction
+ */
+ public static final ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L");
+ /**
+ * M = ~15% correction
+ */
+ public static final ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M");
+ /**
+ * Q = ~25% correction
+ */
+ public static final ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q");
+ /**
+ * H = ~30% correction
+ */
+ public static final ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H");
+
+ private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
+
+ private final int ordinal;
+ private final int bits;
+ private final String name;
+
+ private ErrorCorrectionLevel(int ordinal, int bits, String name) {
+ this.ordinal = ordinal;
+ this.bits = bits;
+ this.name = name;
+ }
+
+ public int ordinal() {
+ return ordinal;
+ }
+
+ public int getBits() {
+ return bits;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * @param bits int containing the two bits encoding a QR Code's error correction level
+ * @return ErrorCorrectionLevel representing the encoded error correction level
+ */
+ public static ErrorCorrectionLevel forBits(int bits) {
+ if (bits < 0 || bits >= FOR_BITS.length) {
+ throw new IllegalArgumentException();
+ }
+ return FOR_BITS[bits];
+ }
+
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
new file mode 100644
index 000000000..1b76b0de5
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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 Sean Owen
+ * @see DataMask
+ * @see ErrorCorrectionLevel
+ */
+final class FormatInformation {
+
+ private static final int FORMAT_INFO_MASK_QR = 0x5412;
+
+ /**
+ * See ISO 18004:2006, Annex C, Table C.1
+ */
+ private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
+ {0x5412, 0x00},
+ {0x5125, 0x01},
+ {0x5E7C, 0x02},
+ {0x5B4B, 0x03},
+ {0x45F9, 0x04},
+ {0x40CE, 0x05},
+ {0x4F97, 0x06},
+ {0x4AA0, 0x07},
+ {0x77C4, 0x08},
+ {0x72F3, 0x09},
+ {0x7DAA, 0x0A},
+ {0x789D, 0x0B},
+ {0x662F, 0x0C},
+ {0x6318, 0x0D},
+ {0x6C41, 0x0E},
+ {0x6976, 0x0F},
+ {0x1689, 0x10},
+ {0x13BE, 0x11},
+ {0x1CE7, 0x12},
+ {0x19D0, 0x13},
+ {0x0762, 0x14},
+ {0x0255, 0x15},
+ {0x0D0C, 0x16},
+ {0x083B, 0x17},
+ {0x355F, 0x18},
+ {0x3068, 0x19},
+ {0x3F31, 0x1A},
+ {0x3A06, 0x1B},
+ {0x24B4, 0x1C},
+ {0x2183, 0x1D},
+ {0x2EDA, 0x1E},
+ {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 byte dataMask;
+
+ private FormatInformation(int formatInfo) {
+ // Bits 3,4
+ errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
+ // Bottom 3 bits
+ dataMask = (byte) (formatInfo & 0x07);
+ }
+
+ static int numBitsDiffering(int a, int b) {
+ 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)] +
+ 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)];
+ }
+
+ /**
+ * @param maskedFormatInfo1 format info indicator, with mask still applied
+ * @param maskedFormatInfo2 second copy of same info; both are checked at the same time
+ * to establish best match
+ * @return information about the format it specifies, or <code>null</code>
+ * if doesn't seem to match any known pattern
+ */
+ static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
+ FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
+ if (formatInfo != null) {
+ return formatInfo;
+ }
+ // Should return null, but, some QR codes apparently
+ // do not mask this info. Try again by actually masking the pattern
+ // first
+ return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
+ maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
+ }
+
+ private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
+ // 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 == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
+ // Found an exact match
+ return new FormatInformation(decodeInfo[1]);
+ }
+ int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
+ if (bitsDifference < bestDifference) {
+ bestFormatInfo = decodeInfo[1];
+ bestDifference = bitsDifference;
+ }
+ if (maskedFormatInfo1 != maskedFormatInfo2) {
+ // also try the other option
+ bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
+ if (bitsDifference < bestDifference) {
+ bestFormatInfo = decodeInfo[1];
+ bestDifference = bitsDifference;
+ }
+ }
+ }
+ // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
+ // differing means we found a match
+ if (bestDifference <= 3) {
+ return new FormatInformation(bestFormatInfo);
+ }
+ return null;
+ }
+
+ ErrorCorrectionLevel getErrorCorrectionLevel() {
+ return errorCorrectionLevel;
+ }
+
+ byte getDataMask() {
+ return dataMask;
+ }
+
+ public int hashCode() {
+ return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof FormatInformation)) {
+ return false;
+ }
+ FormatInformation other = (FormatInformation) o;
+ return this.errorCorrectionLevel == other.errorCorrectionLevel &&
+ this.dataMask == other.dataMask;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
new file mode 100644
index 000000000..3c66217d3
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+/**
+ * <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
+ * data can be encoded to bits in the QR code standard.</p>
+ *
+ * @author Sean Owen
+ */
+public final class Mode {
+
+ // No, we can't use an enum here. J2ME doesn't support it.
+
+ public static final Mode TERMINATOR = new Mode(new int[]{0, 0, 0}, 0x00, "TERMINATOR"); // Not really a mode...
+ public static final Mode NUMERIC = new Mode(new int[]{10, 12, 14}, 0x01, "NUMERIC");
+ public static final Mode ALPHANUMERIC = new Mode(new int[]{9, 11, 13}, 0x02, "ALPHANUMERIC");
+ public static final Mode STRUCTURED_APPEND = new Mode(new int[]{0, 0, 0}, 0x03, "STRUCTURED_APPEND"); // Not supported
+ public static final Mode BYTE = new Mode(new int[]{8, 16, 16}, 0x04, "BYTE");
+ public static final Mode ECI = new Mode(null, 0x07, "ECI"); // character counts don't apply
+ public static final Mode KANJI = new Mode(new int[]{8, 10, 12}, 0x08, "KANJI");
+ public static final Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, "FNC1_FIRST_POSITION");
+ public static final Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, "FNC1_SECOND_POSITION");
+ /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
+ public static final Mode HANZI = new Mode(new int[]{8, 10, 12}, 0x0D, "HANZI");
+
+ private final int[] characterCountBitsForVersions;
+ private final int bits;
+ private final String name;
+
+ private Mode(int[] characterCountBitsForVersions, int bits, String name) {
+ this.characterCountBitsForVersions = characterCountBitsForVersions;
+ this.bits = bits;
+ this.name = name;
+ }
+
+ /**
+ * @param bits four bits encoding a QR Code data mode
+ * @return Mode encoded by these bits
+ * @throws IllegalArgumentException if bits do not correspond to a known mode
+ */
+ public static Mode forBits(int bits) {
+ switch (bits) {
+ case 0x0:
+ return TERMINATOR;
+ case 0x1:
+ return NUMERIC;
+ case 0x2:
+ return ALPHANUMERIC;
+ case 0x3:
+ return STRUCTURED_APPEND;
+ case 0x4:
+ return BYTE;
+ case 0x5:
+ return FNC1_FIRST_POSITION;
+ case 0x7:
+ return ECI;
+ case 0x8:
+ return KANJI;
+ case 0x9:
+ return FNC1_SECOND_POSITION;
+ case 0xD:
+ // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
+ return HANZI;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * @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 Mode
+ */
+ public int getCharacterCountBits(Version version) {
+ if (characterCountBitsForVersions == null) {
+ throw new IllegalArgumentException("Character count doesn't apply to this mode");
+ }
+ int number = version.getVersionNumber();
+ int offset;
+ if (number <= 9) {
+ offset = 0;
+ } else if (number <= 26) {
+ offset = 1;
+ } else {
+ offset = 2;
+ }
+ return characterCountBitsForVersions[offset];
+ }
+
+ public int getBits() {
+ return bits;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
new file mode 100644
index 000000000..ba795de42
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.decoder;
+
+import com.google.zxing.FormatException;
+import com.google.zxing.common.BitMatrix;
+
+/**
+ * See ISO 18004:2006 Annex D
+ *
+ * @author Sean Owen
+ */
+public final class Version {
+
+ /**
+ * See ISO 18004:2006 Annex D.
+ * Element i represents the raw version bits that specify version i + 7
+ */
+ private static final int[] VERSION_DECODE_INFO = {
+ 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
+ 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
+ 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
+ 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
+ 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
+ 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
+ 0x2542E, 0x26A64, 0x27541, 0x28C69
+ };
+
+ private static final Version[] VERSIONS = buildVersions();
+
+ private final int versionNumber;
+ private final int[] alignmentPatternCenters;
+ private final ECBlocks[] ecBlocks;
+ private final int totalCodewords;
+
+ private Version(int versionNumber,
+ int[] alignmentPatternCenters,
+ ECBlocks ecBlocks1,
+ ECBlocks ecBlocks2,
+ ECBlocks ecBlocks3,
+ ECBlocks ecBlocks4) {
+ this.versionNumber = versionNumber;
+ this.alignmentPatternCenters = alignmentPatternCenters;
+ this.ecBlocks = new ECBlocks[]{ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4};
+ int total = 0;
+ int ecCodewords = ecBlocks1.getECCodewordsPerBlock();
+ ECB[] ecbArray = ecBlocks1.getECBlocks();
+ for (int i = 0; i < ecbArray.length; i++) {
+ ECB ecBlock = ecbArray[i];
+ total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
+ }
+ this.totalCodewords = total;
+ }
+
+ public int getVersionNumber() {
+ return versionNumber;
+ }
+
+ public int[] getAlignmentPatternCenters() {
+ return alignmentPatternCenters;
+ }
+
+ public int getTotalCodewords() {
+ return totalCodewords;
+ }
+
+ public int getDimensionForVersion() {
+ return 17 + 4 * versionNumber;
+ }
+
+ public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
+ return ecBlocks[ecLevel.ordinal()];
+ }
+
+ /**
+ * <p>Deduces version information purely from QR Code dimensions.</p>
+ *
+ * @param dimension dimension in modules
+ * @return Version for a QR Code of that dimension
+ * @throws FormatException if dimension is not 1 mod 4
+ */
+ public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {
+ if (dimension % 4 != 1) {
+ throw FormatException.getFormatInstance();
+ }
+ try {
+ return getVersionForNumber((dimension - 17) >> 2);
+ } catch (IllegalArgumentException iae) {
+ throw FormatException.getFormatInstance();
+ }
+ }
+
+ public static Version getVersionForNumber(int versionNumber) {
+ if (versionNumber < 1 || versionNumber > 40) {
+ throw new IllegalArgumentException();
+ }
+ return VERSIONS[versionNumber - 1];
+ }
+
+ static Version decodeVersionInformation(int versionBits) {
+ 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);
+ }
+ // 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;
+ bestDifference = bitsDifference;
+ }
+ }
+ // We can tolerate up to 3 bits of error since no two version info codewords will
+ // differ in less than 8 bits.
+ if (bestDifference <= 3) {
+ return getVersionForNumber(bestVersion);
+ }
+ // If we didn't find a close enough match, fail
+ return null;
+ }
+
+ /**
+ * See ISO 18004:2006 Annex E
+ */
+ BitMatrix buildFunctionPattern() {
+ int dimension = getDimensionForVersion();
+ BitMatrix bitMatrix = new BitMatrix(dimension);
+
+ // Top left finder pattern + separator + format
+ bitMatrix.setRegion(0, 0, 9, 9);
+ // Top right finder pattern + separator + format
+ bitMatrix.setRegion(dimension - 8, 0, 8, 9);
+ // Bottom left finder pattern + separator + format
+ bitMatrix.setRegion(0, dimension - 8, 9, 8);
+
+ // Alignment patterns
+ int max = alignmentPatternCenters.length;
+ for (int x = 0; x < max; x++) {
+ int i = alignmentPatternCenters[x] - 2;
+ for (int y = 0; y < max; y++) {
+ if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
+ // No alignment patterns near the three finder paterns
+ continue;
+ }
+ bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
+ }
+ }
+
+ // Vertical timing pattern
+ bitMatrix.setRegion(6, 9, 1, dimension - 17);
+ // Horizontal timing pattern
+ bitMatrix.setRegion(9, 6, dimension - 17, 1);
+
+ if (versionNumber > 6) {
+ // Version info, top right
+ bitMatrix.setRegion(dimension - 11, 0, 3, 6);
+ // Version info, bottom left
+ bitMatrix.setRegion(0, dimension - 11, 6, 3);
+ }
+
+ return bitMatrix;
+ }
+
+ /**
+ * <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
+ * use blocks of differing sizes within one version, so, this encapsulates the parameters for
+ * each set of blocks. It also holds the number of error-correction codewords per block since it
+ * will be the same across all blocks within one version.</p>
+ */
+ public static final class ECBlocks {
+ private final int ecCodewordsPerBlock;
+ private final ECB[] ecBlocks;
+
+ ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks) {
+ this.ecCodewordsPerBlock = ecCodewordsPerBlock;
+ this.ecBlocks = new ECB[]{ecBlocks};
+ }
+
+ ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks1, ECB ecBlocks2) {
+ this.ecCodewordsPerBlock = ecCodewordsPerBlock;
+ this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2};
+ }
+
+ public int getECCodewordsPerBlock() {
+ return ecCodewordsPerBlock;
+ }
+
+ public int getNumBlocks() {
+ int total = 0;
+ for (int i = 0; i < ecBlocks.length; i++) {
+ total += ecBlocks[i].getCount();
+ }
+ return total;
+ }
+
+ public int getTotalECCodewords() {
+ return ecCodewordsPerBlock * getNumBlocks();
+ }
+
+ public ECB[] getECBlocks() {
+ return ecBlocks;
+ }
+ }
+
+ /**
+ * <p>Encapsualtes the parameters for one error-correction block in one symbol version.
+ * This includes the number of data codewords, and the number of times a block with these
+ * parameters is used consecutively in the QR code version's format.</p>
+ */
+ public static final class ECB {
+ private final int count;
+ private final int dataCodewords;
+
+ ECB(int count, int dataCodewords) {
+ this.count = count;
+ this.dataCodewords = dataCodewords;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public int getDataCodewords() {
+ return dataCodewords;
+ }
+ }
+
+ public String toString() {
+ return String.valueOf(versionNumber);
+ }
+
+ /**
+ * See ISO 18004:2006 6.5.1 Table 9
+ */
+ private static Version[] buildVersions() {
+ return new Version[]{
+ new Version(1, new int[]{},
+ new ECBlocks(7, new ECB(1, 19)),
+ new ECBlocks(10, new ECB(1, 16)),
+ new ECBlocks(13, new ECB(1, 13)),
+ new ECBlocks(17, new ECB(1, 9))),
+ new Version(2, new int[]{6, 18},
+ new ECBlocks(10, new ECB(1, 34)),
+ new ECBlocks(16, new ECB(1, 28)),
+ new ECBlocks(22, new ECB(1, 22)),
+ new ECBlocks(28, new ECB(1, 16))),
+ new Version(3, new int[]{6, 22},
+ new ECBlocks(15, new ECB(1, 55)),
+ new ECBlocks(26, new ECB(1, 44)),
+ new ECBlocks(18, new ECB(2, 17)),
+ new ECBlocks(22, new ECB(2, 13))),
+ new Version(4, new int[]{6, 26},
+ new ECBlocks(20, new ECB(1, 80)),
+ new ECBlocks(18, new ECB(2, 32)),
+ new ECBlocks(26, new ECB(2, 24)),
+ new ECBlocks(16, new ECB(4, 9))),
+ new Version(5, new int[]{6, 30},
+ new ECBlocks(26, new ECB(1, 108)),
+ new ECBlocks(24, new ECB(2, 43)),
+ new ECBlocks(18, new ECB(2, 15),
+ new ECB(2, 16)),
+ new ECBlocks(22, new ECB(2, 11),
+ new ECB(2, 12))),
+ new Version(6, new int[]{6, 34},
+ new ECBlocks(18, new ECB(2, 68)),
+ new ECBlocks(16, new ECB(4, 27)),
+ new ECBlocks(24, new ECB(4, 19)),
+ new ECBlocks(28, new ECB(4, 15))),
+ new Version(7, new int[]{6, 22, 38},
+ new ECBlocks(20, new ECB(2, 78)),
+ new ECBlocks(18, new ECB(4, 31)),
+ new ECBlocks(18, new ECB(2, 14),
+ new ECB(4, 15)),
+ new ECBlocks(26, new ECB(4, 13),
+ new ECB(1, 14))),
+ new Version(8, new int[]{6, 24, 42},
+ new ECBlocks(24, new ECB(2, 97)),
+ new ECBlocks(22, new ECB(2, 38),
+ new ECB(2, 39)),
+ new ECBlocks(22, new ECB(4, 18),
+ new ECB(2, 19)),
+ new ECBlocks(26, new ECB(4, 14),
+ new ECB(2, 15))),
+ new Version(9, new int[]{6, 26, 46},
+ new ECBlocks(30, new ECB(2, 116)),
+ new ECBlocks(22, new ECB(3, 36),
+ new ECB(2, 37)),
+ new ECBlocks(20, new ECB(4, 16),
+ new ECB(4, 17)),
+ new ECBlocks(24, new ECB(4, 12),
+ new ECB(4, 13))),
+ new Version(10, new int[]{6, 28, 50},
+ new ECBlocks(18, new ECB(2, 68),
+ new ECB(2, 69)),
+ new ECBlocks(26, new ECB(4, 43),
+ new ECB(1, 44)),
+ new ECBlocks(24, new ECB(6, 19),
+ new ECB(2, 20)),
+ new ECBlocks(28, new ECB(6, 15),
+ new ECB(2, 16))),
+ new Version(11, new int[]{6, 30, 54},
+ new ECBlocks(20, new ECB(4, 81)),
+ new ECBlocks(30, new ECB(1, 50),
+ new ECB(4, 51)),
+ new ECBlocks(28, new ECB(4, 22),
+ new ECB(4, 23)),
+ new ECBlocks(24, new ECB(3, 12),
+ new ECB(8, 13))),
+ new Version(12, new int[]{6, 32, 58},
+ new ECBlocks(24, new ECB(2, 92),
+ new ECB(2, 93)),
+ new ECBlocks(22, new ECB(6, 36),
+ new ECB(2, 37)),
+ new ECBlocks(26, new ECB(4, 20),
+ new ECB(6, 21)),
+ new ECBlocks(28, new ECB(7, 14),
+ new ECB(4, 15))),
+ new Version(13, new int[]{6, 34, 62},
+ new ECBlocks(26, new ECB(4, 107)),
+ new ECBlocks(22, new ECB(8, 37),
+ new ECB(1, 38)),
+ new ECBlocks(24, new ECB(8, 20),
+ new ECB(4, 21)),
+ new ECBlocks(22, new ECB(12, 11),
+ new ECB(4, 12))),
+ new Version(14, new int[]{6, 26, 46, 66},
+ new ECBlocks(30, new ECB(3, 115),
+ new ECB(1, 116)),
+ new ECBlocks(24, new ECB(4, 40),
+ new ECB(5, 41)),
+ new ECBlocks(20, new ECB(11, 16),
+ new ECB(5, 17)),
+ new ECBlocks(24, new ECB(11, 12),
+ new ECB(5, 13))),
+ new Version(15, new int[]{6, 26, 48, 70},
+ new ECBlocks(22, new ECB(5, 87),
+ new ECB(1, 88)),
+ new ECBlocks(24, new ECB(5, 41),
+ new ECB(5, 42)),
+ new ECBlocks(30, new ECB(5, 24),
+ new ECB(7, 25)),
+ new ECBlocks(24, new ECB(11, 12),
+ new ECB(7, 13))),
+ new Version(16, new int[]{6, 26, 50, 74},
+ new ECBlocks(24, new ECB(5, 98),
+ new ECB(1, 99)),
+ new ECBlocks(28, new ECB(7, 45),
+ new ECB(3, 46)),
+ new ECBlocks(24, new ECB(15, 19),
+ new ECB(2, 20)),
+ new ECBlocks(30, new ECB(3, 15),
+ new ECB(13, 16))),
+ new Version(17, new int[]{6, 30, 54, 78},
+ new ECBlocks(28, new ECB(1, 107),
+ new ECB(5, 108)),
+ new ECBlocks(28, new ECB(10, 46),
+ new ECB(1, 47)),
+ new ECBlocks(28, new ECB(1, 22),
+ new ECB(15, 23)),
+ new ECBlocks(28, new ECB(2, 14),
+ new ECB(17, 15))),
+ new Version(18, new int[]{6, 30, 56, 82},
+ new ECBlocks(30, new ECB(5, 120),
+ new ECB(1, 121)),
+ new ECBlocks(26, new ECB(9, 43),
+ new ECB(4, 44)),
+ new ECBlocks(28, new ECB(17, 22),
+ new ECB(1, 23)),
+ new ECBlocks(28, new ECB(2, 14),
+ new ECB(19, 15))),
+ new Version(19, new int[]{6, 30, 58, 86},
+ new ECBlocks(28, new ECB(3, 113),
+ new ECB(4, 114)),
+ new ECBlocks(26, new ECB(3, 44),
+ new ECB(11, 45)),
+ new ECBlocks(26, new ECB(17, 21),
+ new ECB(4, 22)),
+ new ECBlocks(26, new ECB(9, 13),
+ new ECB(16, 14))),
+ new Version(20, new int[]{6, 34, 62, 90},
+ new ECBlocks(28, new ECB(3, 107),
+ new ECB(5, 108)),
+ new ECBlocks(26, new ECB(3, 41),
+ new ECB(13, 42)),
+ new ECBlocks(30, new ECB(15, 24),
+ new ECB(5, 25)),
+ new ECBlocks(28, new ECB(15, 15),
+ new ECB(10, 16))),
+ new Version(21, new int[]{6, 28, 50, 72, 94},
+ new ECBlocks(28, new ECB(4, 116),
+ new ECB(4, 117)),
+ new ECBlocks(26, new ECB(17, 42)),
+ new ECBlocks(28, new ECB(17, 22),
+ new ECB(6, 23)),
+ new ECBlocks(30, new ECB(19, 16),
+ new ECB(6, 17))),
+ new Version(22, new int[]{6, 26, 50, 74, 98},
+ new ECBlocks(28, new ECB(2, 111),
+ new ECB(7, 112)),
+ new ECBlocks(28, new ECB(17, 46)),
+ new ECBlocks(30, new ECB(7, 24),
+ new ECB(16, 25)),
+ new ECBlocks(24, new ECB(34, 13))),
+ new Version(23, new int[]{6, 30, 54, 78, 102},
+ new ECBlocks(30, new ECB(4, 121),
+ new ECB(5, 122)),
+ new ECBlocks(28, new ECB(4, 47),
+ new ECB(14, 48)),
+ new ECBlocks(30, new ECB(11, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(16, 15),
+ new ECB(14, 16))),
+ new Version(24, new int[]{6, 28, 54, 80, 106},
+ new ECBlocks(30, new ECB(6, 117),
+ new ECB(4, 118)),
+ new ECBlocks(28, new ECB(6, 45),
+ new ECB(14, 46)),
+ new ECBlocks(30, new ECB(11, 24),
+ new ECB(16, 25)),
+ new ECBlocks(30, new ECB(30, 16),
+ new ECB(2, 17))),
+ new Version(25, new int[]{6, 32, 58, 84, 110},
+ new ECBlocks(26, new ECB(8, 106),
+ new ECB(4, 107)),
+ new ECBlocks(28, new ECB(8, 47),
+ new ECB(13, 48)),
+ new ECBlocks(30, new ECB(7, 24),
+ new ECB(22, 25)),
+ new ECBlocks(30, new ECB(22, 15),
+ new ECB(13, 16))),
+ new Version(26, new int[]{6, 30, 58, 86, 114},
+ new ECBlocks(28, new ECB(10, 114),
+ new ECB(2, 115)),
+ new ECBlocks(28, new ECB(19, 46),
+ new ECB(4, 47)),
+ new ECBlocks(28, new ECB(28, 22),
+ new ECB(6, 23)),
+ new ECBlocks(30, new ECB(33, 16),
+ new ECB(4, 17))),
+ new Version(27, new int[]{6, 34, 62, 90, 118},
+ new ECBlocks(30, new ECB(8, 122),
+ new ECB(4, 123)),
+ new ECBlocks(28, new ECB(22, 45),
+ new ECB(3, 46)),
+ new ECBlocks(30, new ECB(8, 23),
+ new ECB(26, 24)),
+ new ECBlocks(30, new ECB(12, 15),
+ new ECB(28, 16))),
+ new Version(28, new int[]{6, 26, 50, 74, 98, 122},
+ new ECBlocks(30, new ECB(3, 117),
+ new ECB(10, 118)),
+ new ECBlocks(28, new ECB(3, 45),
+ new ECB(23, 46)),
+ new ECBlocks(30, new ECB(4, 24),
+ new ECB(31, 25)),
+ new ECBlocks(30, new ECB(11, 15),
+ new ECB(31, 16))),
+ new Version(29, new int[]{6, 30, 54, 78, 102, 126},
+ new ECBlocks(30, new ECB(7, 116),
+ new ECB(7, 117)),
+ new ECBlocks(28, new ECB(21, 45),
+ new ECB(7, 46)),
+ new ECBlocks(30, new ECB(1, 23),
+ new ECB(37, 24)),
+ new ECBlocks(30, new ECB(19, 15),
+ new ECB(26, 16))),
+ new Version(30, new int[]{6, 26, 52, 78, 104, 130},
+ new ECBlocks(30, new ECB(5, 115),
+ new ECB(10, 116)),
+ new ECBlocks(28, new ECB(19, 47),
+ new ECB(10, 48)),
+ new ECBlocks(30, new ECB(15, 24),
+ new ECB(25, 25)),
+ new ECBlocks(30, new ECB(23, 15),
+ new ECB(25, 16))),
+ new Version(31, new int[]{6, 30, 56, 82, 108, 134},
+ new ECBlocks(30, new ECB(13, 115),
+ new ECB(3, 116)),
+ new ECBlocks(28, new ECB(2, 46),
+ new ECB(29, 47)),
+ new ECBlocks(30, new ECB(42, 24),
+ new ECB(1, 25)),
+ new ECBlocks(30, new ECB(23, 15),
+ new ECB(28, 16))),
+ new Version(32, new int[]{6, 34, 60, 86, 112, 138},
+ new ECBlocks(30, new ECB(17, 115)),
+ new ECBlocks(28, new ECB(10, 46),
+ new ECB(23, 47)),
+ new ECBlocks(30, new ECB(10, 24),
+ new ECB(35, 25)),
+ new ECBlocks(30, new ECB(19, 15),
+ new ECB(35, 16))),
+ new Version(33, new int[]{6, 30, 58, 86, 114, 142},
+ new ECBlocks(30, new ECB(17, 115),
+ new ECB(1, 116)),
+ new ECBlocks(28, new ECB(14, 46),
+ new ECB(21, 47)),
+ new ECBlocks(30, new ECB(29, 24),
+ new ECB(19, 25)),
+ new ECBlocks(30, new ECB(11, 15),
+ new ECB(46, 16))),
+ new Version(34, new int[]{6, 34, 62, 90, 118, 146},
+ new ECBlocks(30, new ECB(13, 115),
+ new ECB(6, 116)),
+ new ECBlocks(28, new ECB(14, 46),
+ new ECB(23, 47)),
+ new ECBlocks(30, new ECB(44, 24),
+ new ECB(7, 25)),
+ new ECBlocks(30, new ECB(59, 16),
+ new ECB(1, 17))),
+ new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},
+ new ECBlocks(30, new ECB(12, 121),
+ new ECB(7, 122)),
+ new ECBlocks(28, new ECB(12, 47),
+ new ECB(26, 48)),
+ new ECBlocks(30, new ECB(39, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(22, 15),
+ new ECB(41, 16))),
+ new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},
+ new ECBlocks(30, new ECB(6, 121),
+ new ECB(14, 122)),
+ new ECBlocks(28, new ECB(6, 47),
+ new ECB(34, 48)),
+ new ECBlocks(30, new ECB(46, 24),
+ new ECB(10, 25)),
+ new ECBlocks(30, new ECB(2, 15),
+ new ECB(64, 16))),
+ new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},
+ new ECBlocks(30, new ECB(17, 122),
+ new ECB(4, 123)),
+ new ECBlocks(28, new ECB(29, 46),
+ new ECB(14, 47)),
+ new ECBlocks(30, new ECB(49, 24),
+ new ECB(10, 25)),
+ new ECBlocks(30, new ECB(24, 15),
+ new ECB(46, 16))),
+ new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},
+ new ECBlocks(30, new ECB(4, 122),
+ new ECB(18, 123)),
+ new ECBlocks(28, new ECB(13, 46),
+ new ECB(32, 47)),
+ new ECBlocks(30, new ECB(48, 24),
+ new ECB(14, 25)),
+ new ECBlocks(30, new ECB(42, 15),
+ new ECB(32, 16))),
+ new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},
+ new ECBlocks(30, new ECB(20, 117),
+ new ECB(4, 118)),
+ new ECBlocks(28, new ECB(40, 47),
+ new ECB(7, 48)),
+ new ECBlocks(30, new ECB(43, 24),
+ new ECB(22, 25)),
+ new ECBlocks(30, new ECB(10, 15),
+ new ECB(67, 16))),
+ new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},
+ new ECBlocks(30, new ECB(19, 118),
+ new ECB(6, 119)),
+ new ECBlocks(28, new ECB(18, 47),
+ new ECB(31, 48)),
+ new ECBlocks(30, new ECB(34, 24),
+ new ECB(34, 25)),
+ new ECBlocks(30, new ECB(20, 15),
+ new ECB(61, 16)))
+ };
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
new file mode 100644
index 000000000..6fc1a2c88
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+import com.google.zxing.ResultPoint;
+
+/**
+ * <p>Encapsulates an alignment pattern, which are the smaller square patterns found in
+ * all but the simplest QR Codes.</p>
+ *
+ * @author Sean Owen
+ */
+public final class AlignmentPattern extends ResultPoint {
+
+ private final float estimatedModuleSize;
+
+ AlignmentPattern(float posX, float posY, float estimatedModuleSize) {
+ super(posX, posY);
+ this.estimatedModuleSize = estimatedModuleSize;
+ }
+
+ /**
+ * <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
+ */
+ boolean aboutEquals(float moduleSize, float i, float j) {
+ if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
+ float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
+ return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
new file mode 100644
index 000000000..3aadf284f
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+
+import java.util.Vector;
+
+/**
+ * <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
+ * patterns but are smaller and appear at regular intervals throughout the image.</p>
+ *
+ * <p>At the moment this only looks for the bottom-right alignment pattern.</p>
+ *
+ * <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
+ * pasted and stripped down here for maximum performance but does unfortunately duplicate
+ * some code.</p>
+ *
+ * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+final class AlignmentPatternFinder {
+
+ private final BitMatrix image;
+ private final Vector possibleCenters;
+ private final int startX;
+ private final int startY;
+ private final int width;
+ private final int height;
+ private final float moduleSize;
+ private final int[] crossCheckStateCount;
+ private final ResultPointCallback resultPointCallback;
+
+ /**
+ * <p>Creates a finder that will look in a portion of the whole image.</p>
+ *
+ * @param image image to search
+ * @param startX left column from which to start searching
+ * @param startY top row from which to start searching
+ * @param width width of region to search
+ * @param height height of region to search
+ * @param moduleSize estimated module size so far
+ */
+ AlignmentPatternFinder(BitMatrix image,
+ int startX,
+ int startY,
+ int width,
+ int height,
+ float moduleSize,
+ ResultPointCallback resultPointCallback) {
+ this.image = image;
+ this.possibleCenters = new Vector(5);
+ this.startX = startX;
+ this.startY = startY;
+ this.width = width;
+ this.height = height;
+ this.moduleSize = moduleSize;
+ this.crossCheckStateCount = new int[3];
+ this.resultPointCallback = resultPointCallback;
+ }
+
+ /**
+ * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
+ * it's pretty performance-critical and so is written to be fast foremost.</p>
+ *
+ * @return {@link AlignmentPattern} if found
+ * @throws NotFoundException if not found
+ */
+ AlignmentPattern find() throws NotFoundException {
+ int startX = this.startX;
+ int height = this.height;
+ int maxJ = startX + width;
+ int middleI = startY + (height >> 1);
+ // We are looking for black/white/black modules in 1:1:1 ratio;
+ // this tracks the number of black/white/black modules seen so far
+ int[] stateCount = new int[3];
+ for (int iGen = 0; iGen < height; iGen++) {
+ // Search from middle outwards
+ int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) >> 1 : -((iGen + 1) >> 1));
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ int j = startX;
+ // Burn off leading white pixels before anything else; if we start in the middle of
+ // a white run, it doesn't make sense to count its length, since we don't know if the
+ // white run continued to the left of the start point
+ while (j < maxJ && !image.get(j, i)) {
+ j++;
+ }
+ int currentState = 0;
+ while (j < maxJ) {
+ if (image.get(j, i)) {
+ // Black pixel
+ if (currentState == 1) { // Counting black pixels
+ stateCount[currentState]++;
+ } else { // Counting white pixels
+ if (currentState == 2) { // A winner?
+ if (foundPatternCross(stateCount)) { // Yes
+ AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);
+ if (confirmed != null) {
+ return confirmed;
+ }
+ }
+ stateCount[0] = stateCount[2];
+ stateCount[1] = 1;
+ stateCount[2] = 0;
+ currentState = 1;
+ } else {
+ stateCount[++currentState]++;
+ }
+ }
+ } else { // White pixel
+ if (currentState == 1) { // Counting black pixels
+ currentState++;
+ }
+ stateCount[currentState]++;
+ }
+ j++;
+ }
+ if (foundPatternCross(stateCount)) {
+ AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ);
+ if (confirmed != null) {
+ return confirmed;
+ }
+ }
+
+ }
+
+ // Hmm, nothing we saw was observed and confirmed twice. If we had
+ // any guess at all, return it.
+ if (!possibleCenters.isEmpty()) {
+ return (AlignmentPattern) possibleCenters.elementAt(0);
+ }
+
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ /**
+ * Given a count of black/white/black pixels just seen and an end position,
+ * figures the location of the center of this black/white/black run.
+ */
+ private static float centerFromEnd(int[] stateCount, int end) {
+ return (float) (end - stateCount[2]) - stateCount[1] / 2.0f;
+ }
+
+ /**
+ * @param stateCount count of black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
+ * used by alignment patterns to be considered a match
+ */
+ private boolean foundPatternCross(int[] stateCount) {
+ float moduleSize = this.moduleSize;
+ float maxVariance = moduleSize / 2.0f;
+ for (int i = 0; i < 3; i++) {
+ if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * <p>After a horizontal scan finds a potential alignment pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * alignment pattern to see if the same proportion is detected.</p>
+ *
+ * @param startI row where an alignment pattern was detected
+ * @param centerJ center of the section that appears to cross an alignment pattern
+ * @param maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
+ */
+ private float crossCheckVertical(int startI, int centerJ, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxI = image.getHeight();
+ int[] stateCount = crossCheckStateCount;
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+
+ // Start counting up from center
+ int i = startI;
+ while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if (i < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ i--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ // Now also count down from center
+ i = startI + 1;
+ while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i++;
+ }
+ if (i == maxI || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {
+ stateCount[2]++;
+ i++;
+ }
+ if (stateCount[2] > maxCount) {
+ return Float.NaN;
+ }
+
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
+ }
+
+ /**
+ * <p>This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will see if this pattern had been
+ * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
+ * found the alignment pattern.</p>
+ *
+ * @param stateCount reading state module counts from horizontal scan
+ * @param i row where alignment pattern may be found
+ * @param j end of possible alignment pattern in row
+ * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
+ */
+ private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];
+ float centerJ = centerFromEnd(stateCount, j);
+ float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);
+ if (!Float.isNaN(centerI)) {
+ float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;
+ int max = possibleCenters.size();
+ for (int index = 0; index < max; index++) {
+ AlignmentPattern center = (AlignmentPattern) possibleCenters.elementAt(index);
+ // Look for about the same center and module size:
+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
+ return new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
+ }
+ }
+ // Hadn't found this before; save it
+ ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
+ possibleCenters.addElement(point);
+ if (resultPointCallback != null) {
+ resultPointCallback.foundPossibleResultPoint(point);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
new file mode 100644
index 000000000..724d39d59
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.common.GridSampler;
+import com.google.zxing.common.PerspectiveTransform;
+import com.google.zxing.qrcode.decoder.Version;
+
+import java.util.Hashtable;
+
+/**
+ * <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.</p>
+ *
+ * @author Sean Owen
+ */
+public class Detector {
+
+ private final BitMatrix image;
+ private ResultPointCallback resultPointCallback;
+
+ public Detector(BitMatrix image) {
+ this.image = image;
+ }
+
+ protected BitMatrix getImage() {
+ return image;
+ }
+
+ protected ResultPointCallback getResultPointCallback() {
+ return resultPointCallback;
+ }
+
+ /**
+ * <p>Detects a QR Code in an image, simply.</p>
+ *
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if no QR Code can be found
+ */
+ public DetectorResult detect() throws NotFoundException, FormatException {
+ return detect(null);
+ }
+
+ /**
+ * <p>Detects a QR Code in an image, simply.</p>
+ *
+ * @param hints optional hints to detector
+ * @return {@link NotFoundException} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if QR Code cannot be found
+ * @throws FormatException if a QR Code cannot be decoded
+ */
+ public DetectorResult detect(Hashtable hints) throws NotFoundException, FormatException {
+
+ resultPointCallback = hints == null ? null :
+ (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+
+ FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);
+ FinderPatternInfo info = finder.find(hints);
+
+ return processFinderPatternInfo(info);
+ }
+
+ protected DetectorResult processFinderPatternInfo(FinderPatternInfo info)
+ throws NotFoundException, FormatException {
+
+ FinderPattern topLeft = info.getTopLeft();
+ FinderPattern topRight = info.getTopRight();
+ FinderPattern bottomLeft = info.getBottomLeft();
+
+ float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft);
+ if (moduleSize < 1.0f) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+ int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize);
+ Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension);
+ int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7;
+
+ AlignmentPattern alignmentPattern = null;
+ // Anything above version 1 has an alignment pattern
+ if (provisionalVersion.getAlignmentPatternCenters().length > 0) {
+
+ // Guess where a "bottom right" finder pattern would have been
+ float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX();
+ float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY();
+
+ // Estimate that alignment pattern is closer by 3 modules
+ // from "bottom right" to known top left location
+ float correctionToTopLeft = 1.0f - 3.0f / (float) modulesBetweenFPCenters;
+ int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX()));
+ int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY()));
+
+ // Kind of arbitrary -- expand search radius before giving up
+ for (int i = 4; i <= 16; i <<= 1) {
+ try {
+ alignmentPattern = findAlignmentInRegion(moduleSize,
+ estAlignmentX,
+ estAlignmentY,
+ (float) i);
+ break;
+ } catch (NotFoundException re) {
+ // try next round
+ }
+ }
+ // If we didn't find alignment pattern... well try anyway without it
+ }
+
+ PerspectiveTransform transform =
+ createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension);
+
+ BitMatrix bits = sampleGrid(image, transform, dimension);
+
+ ResultPoint[] points;
+ if (alignmentPattern == null) {
+ points = new ResultPoint[]{bottomLeft, topLeft, topRight};
+ } else {
+ points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};
+ }
+ return new DetectorResult(bits, points);
+ }
+
+ public static PerspectiveTransform createTransform(ResultPoint topLeft,
+ ResultPoint topRight,
+ ResultPoint bottomLeft,
+ ResultPoint alignmentPattern,
+ int dimension) {
+ float dimMinusThree = (float) dimension - 3.5f;
+ float bottomRightX;
+ float bottomRightY;
+ float sourceBottomRightX;
+ float sourceBottomRightY;
+ if (alignmentPattern != null) {
+ bottomRightX = alignmentPattern.getX();
+ bottomRightY = alignmentPattern.getY();
+ sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0f;
+ } else {
+ // Don't have an alignment pattern, just make up the bottom-right point
+ bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX();
+ bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY();
+ sourceBottomRightX = sourceBottomRightY = dimMinusThree;
+ }
+
+ return PerspectiveTransform.quadrilateralToQuadrilateral(
+ 3.5f,
+ 3.5f,
+ dimMinusThree,
+ 3.5f,
+ sourceBottomRightX,
+ sourceBottomRightY,
+ 3.5f,
+ dimMinusThree,
+ topLeft.getX(),
+ topLeft.getY(),
+ topRight.getX(),
+ topRight.getY(),
+ bottomRightX,
+ bottomRightY,
+ bottomLeft.getX(),
+ bottomLeft.getY());
+ }
+
+ private static BitMatrix sampleGrid(BitMatrix image,
+ PerspectiveTransform transform,
+ int dimension) throws NotFoundException {
+
+ GridSampler sampler = GridSampler.getInstance();
+ return sampler.sampleGrid(image, dimension, dimension, transform);
+ }
+
+ /**
+ * <p>Computes the dimension (number of modules on a size) of the QR Code based on the position
+ * of the finder patterns and estimated module size.</p>
+ */
+ protected static int computeDimension(ResultPoint topLeft,
+ ResultPoint topRight,
+ ResultPoint bottomLeft,
+ float moduleSize) throws NotFoundException {
+ int tltrCentersDimension = round(ResultPoint.distance(topLeft, topRight) / moduleSize);
+ int tlblCentersDimension = round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize);
+ int dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7;
+ switch (dimension & 0x03) { // mod 4
+ case 0:
+ dimension++;
+ break;
+ // 1? do nothing
+ case 2:
+ dimension--;
+ break;
+ case 3:
+ throw NotFoundException.getNotFoundInstance();
+ }
+ return dimension;
+ }
+
+ /**
+ * <p>Computes an average estimated module size based on estimated derived from the positions
+ * of the three finder patterns.</p>
+ */
+ protected float calculateModuleSize(ResultPoint topLeft,
+ ResultPoint topRight,
+ ResultPoint bottomLeft) {
+ // Take the average
+ return (calculateModuleSizeOneWay(topLeft, topRight) +
+ calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f;
+ }
+
+ /**
+ * <p>Estimates module size based on two finder patterns -- it uses
+ * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
+ * width of each, measuring along the axis between their centers.</p>
+ */
+ private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) {
+ float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(),
+ (int) pattern.getY(),
+ (int) otherPattern.getX(),
+ (int) otherPattern.getY());
+ float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(),
+ (int) otherPattern.getY(),
+ (int) pattern.getX(),
+ (int) pattern.getY());
+ if (Float.isNaN(moduleSizeEst1)) {
+ return moduleSizeEst2 / 7.0f;
+ }
+ if (Float.isNaN(moduleSizeEst2)) {
+ return moduleSizeEst1 / 7.0f;
+ }
+ // Average them, and divide by 7 since we've counted the width of 3 black modules,
+ // and 1 white and 1 black module on either side. Ergo, divide sum by 14.
+ return (moduleSizeEst1 + moduleSizeEst2) / 14.0f;
+ }
+
+ /**
+ * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
+ * a finder pattern by looking for a black-white-black run from the center in the direction
+ * of another point (another finder pattern center), and in the opposite direction too.</p>
+ */
+ private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) {
+
+ float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY);
+
+ // Now count other way -- don't run off image though of course
+ float scale = 1.0f;
+ int otherToX = fromX - (toX - fromX);
+ if (otherToX < 0) {
+ scale = (float) fromX / (float) (fromX - otherToX);
+ otherToX = 0;
+ } else if (otherToX > image.getWidth()) {
+ scale = (float) (image.getWidth() - fromX) / (float) (otherToX - fromX);
+ otherToX = image.getWidth();
+ }
+ int otherToY = (int) (fromY - (toY - fromY) * scale);
+
+ scale = 1.0f;
+ if (otherToY < 0) {
+ scale = (float) fromY / (float) (fromY - otherToY);
+ otherToY = 0;
+ } else if (otherToY > image.getHeight()) {
+ scale = (float) (image.getHeight() - fromY) / (float) (otherToY - fromY);
+ otherToY = image.getHeight();
+ }
+ otherToX = (int) (fromX + (otherToX - fromX) * scale);
+
+ result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY);
+ return result;
+ }
+
+ /**
+ * <p>This method traces a line from a point in the image, in the direction towards another point.
+ * It begins in a black region, and keeps going until it finds white, then black, then white again.
+ * It reports the distance from the start to this point.</p>
+ *
+ * <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
+ * may be skewed or rotated.</p>
+ */
+ private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) {
+ // Mild variant of Bresenham's algorithm;
+ // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
+ boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX);
+ if (steep) {
+ int temp = fromX;
+ fromX = fromY;
+ fromY = temp;
+ temp = toX;
+ toX = toY;
+ toY = temp;
+ }
+
+ int dx = Math.abs(toX - fromX);
+ int dy = Math.abs(toY - fromY);
+ int error = -dx >> 1;
+ int xstep = fromX < toX ? 1 : -1;
+ int ystep = fromY < toY ? 1 : -1;
+
+ // In black pixels, looking for white, first or second time.
+ int state = 0;
+ for (int x = fromX, y = fromY; x != toX; x += xstep) {
+ int realX = steep ? y : x;
+ int realY = steep ? x : y;
+
+ // In white pixels, looking for black.
+ // FIXME(dswitkin): This method seems to assume square images, which can cause these calls to
+ // BitMatrix.get() to throw ArrayIndexOutOfBoundsException.
+ if (state == 1) {
+ if (image.get(realX, realY)) {
+ state++;
+ }
+ } else {
+ if (!image.get(realX, realY)) {
+ state++;
+ }
+ }
+
+ // Found black, white, black, and stumbled back onto white, so we're done.
+ if (state == 3) {
+ int diffX = x - fromX;
+ int diffY = y - fromY;
+ if (xstep < 0) {
+ diffX++;
+ }
+ return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
+ }
+ error += dy;
+ if (error > 0) {
+ if (y == toY) {
+ break;
+ }
+ y += ystep;
+ error -= dx;
+ }
+ }
+ int diffX = toX - fromX;
+ int diffY = toY - fromY;
+ return (float) Math.sqrt((double) (diffX * diffX + diffY * diffY));
+ }
+
+ /**
+ * <p>Attempts to locate an alignment pattern in a limited region of the image, which is
+ * guessed to contain it. This method uses {@link AlignmentPattern}.</p>
+ *
+ * @param overallEstModuleSize estimated module size so far
+ * @param estAlignmentX x coordinate of center of area probably containing alignment pattern
+ * @param estAlignmentY y coordinate of above
+ * @param allowanceFactor number of pixels in all directions to search from the center
+ * @return {@link AlignmentPattern} if found, or null otherwise
+ * @throws NotFoundException if an unexpected error occurs during detection
+ */
+ protected AlignmentPattern findAlignmentInRegion(float overallEstModuleSize,
+ int estAlignmentX,
+ int estAlignmentY,
+ float allowanceFactor)
+ throws NotFoundException {
+ // Look for an alignment pattern (3 modules in size) around where it
+ // should be
+ int allowance = (int) (allowanceFactor * overallEstModuleSize);
+ int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance);
+ int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance);
+ if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance);
+ int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance);
+ if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) {
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ AlignmentPatternFinder alignmentFinder =
+ new AlignmentPatternFinder(
+ image,
+ alignmentAreaLeftX,
+ alignmentAreaTopY,
+ alignmentAreaRightX - alignmentAreaLeftX,
+ alignmentAreaBottomY - alignmentAreaTopY,
+ overallEstModuleSize,
+ resultPointCallback);
+ return alignmentFinder.find();
+ }
+
+ /**
+ * Ends up being a bit faster than Math.round(). This merely rounds its argument to the nearest int,
+ * where x.5 rounds up.
+ */
+ private static int round(float d) {
+ return (int) (d + 0.5f);
+ }
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
new file mode 100644
index 000000000..7a9914d76
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+import com.google.zxing.ResultPoint;
+
+/**
+ * <p>Encapsulates a finder pattern, which are the three square patterns found in
+ * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
+ * as a convenience to the finder's bookkeeping.</p>
+ *
+ * @author Sean Owen
+ */
+public final class FinderPattern extends ResultPoint {
+
+ private final float estimatedModuleSize;
+ private int count;
+
+ FinderPattern(float posX, float posY, float estimatedModuleSize) {
+ super(posX, posY);
+ this.estimatedModuleSize = estimatedModuleSize;
+ this.count = 1;
+ }
+
+ public float getEstimatedModuleSize() {
+ return estimatedModuleSize;
+ }
+
+ int getCount() {
+ return count;
+ }
+
+ void incrementCount() {
+ this.count++;
+ }
+
+ /**
+ * <p>Determines if this finder pattern "about equals" a finder pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
+ */
+ boolean aboutEquals(float moduleSize, float i, float j) {
+ if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) {
+ float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize);
+ return moduleSizeDiff <= 1.0f || moduleSizeDiff / estimatedModuleSize <= 1.0f;
+ }
+ return false;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
new file mode 100644
index 000000000..01b3bde2a
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.Collections;
+import com.google.zxing.common.Comparator;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
+ * markers at three corners of a QR Code.</p>
+ *
+ * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+public class FinderPatternFinder {
+
+ private static final int CENTER_QUORUM = 2;
+ protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
+ protected static final int MAX_MODULES = 57; // support up to version 10 for mobile clients
+ private static final int INTEGER_MATH_SHIFT = 8;
+
+ private final BitMatrix image;
+ private final Vector possibleCenters;
+ private boolean hasSkipped;
+ private final int[] crossCheckStateCount;
+ private final ResultPointCallback resultPointCallback;
+
+ /**
+ * <p>Creates a finder that will search the image for three finder patterns.</p>
+ *
+ * @param image image to search
+ */
+ public FinderPatternFinder(BitMatrix image) {
+ this(image, null);
+ }
+
+ public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
+ this.image = image;
+ this.possibleCenters = new Vector();
+ this.crossCheckStateCount = new int[5];
+ this.resultPointCallback = resultPointCallback;
+ }
+
+ protected BitMatrix getImage() {
+ return image;
+ }
+
+ protected Vector getPossibleCenters() {
+ return possibleCenters;
+ }
+
+ FinderPatternInfo find(Hashtable hints) throws NotFoundException {
+ boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
+ int maxI = image.getHeight();
+ int maxJ = image.getWidth();
+ // We are looking for black/white/black/white/black modules in
+ // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
+
+ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
+ // image, and then account for the center being 3 modules in size. This gives the smallest
+ // number of pixels the center could be, so skip this often. When trying harder, look for all
+ // QR versions regardless of how dense they are.
+ int iSkip = (3 * maxI) / (4 * MAX_MODULES);
+ if (iSkip < MIN_SKIP || tryHarder) {
+ iSkip = MIN_SKIP;
+ }
+
+ boolean done = false;
+ int[] stateCount = new int[5];
+ for (int i = iSkip - 1; i < maxI && !done; i += iSkip) {
+ // Get a row of black/white values
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ int currentState = 0;
+ for (int j = 0; j < maxJ; j++) {
+ if (image.get(j, i)) {
+ // Black pixel
+ if ((currentState & 1) == 1) { // Counting white pixels
+ currentState++;
+ }
+ stateCount[currentState]++;
+ } else { // White pixel
+ if ((currentState & 1) == 0) { // Counting black pixels
+ if (currentState == 4) { // A winner?
+ if (foundPatternCross(stateCount)) { // Yes
+ boolean confirmed = handlePossibleCenter(stateCount, i, j);
+ if (confirmed) {
+ // Start examining every other line. Checking each line turned out to be too
+ // expensive and didn't improve performance.
+ iSkip = 2;
+ if (hasSkipped) {
+ done = haveMultiplyConfirmedCenters();
+ } else {
+ int rowSkip = findRowSkip();
+ if (rowSkip > stateCount[2]) {
+ // Skip rows between row of lower confirmed center
+ // and top of presumed third confirmed center
+ // but back up a bit to get a full chance of detecting
+ // it, entire width of center of finder pattern
+
+ // Skip by rowSkip, but back off by stateCount[2] (size of last center
+ // of pattern we saw) to be conservative, and also back off by iSkip which
+ // is about to be re-added
+ i += rowSkip - stateCount[2] - iSkip;
+ j = maxJ - 1;
+ }
+ }
+ } else {
+ stateCount[0] = stateCount[2];
+ stateCount[1] = stateCount[3];
+ stateCount[2] = stateCount[4];
+ stateCount[3] = 1;
+ stateCount[4] = 0;
+ currentState = 3;
+ continue;
+ }
+ // Clear state to start looking again
+ currentState = 0;
+ stateCount[0] = 0;
+ stateCount[1] = 0;
+ stateCount[2] = 0;
+ stateCount[3] = 0;
+ stateCount[4] = 0;
+ } else { // No, shift counts back by two
+ stateCount[0] = stateCount[2];
+ stateCount[1] = stateCount[3];
+ stateCount[2] = stateCount[4];
+ stateCount[3] = 1;
+ stateCount[4] = 0;
+ currentState = 3;
+ }
+ } else {
+ stateCount[++currentState]++;
+ }
+ } else { // Counting white pixels
+ stateCount[currentState]++;
+ }
+ }
+ }
+ if (foundPatternCross(stateCount)) {
+ boolean confirmed = handlePossibleCenter(stateCount, i, maxJ);
+ if (confirmed) {
+ iSkip = stateCount[0];
+ if (hasSkipped) {
+ // Found a third one
+ done = haveMultiplyConfirmedCenters();
+ }
+ }
+ }
+ }
+
+ FinderPattern[] patternInfo = selectBestPatterns();
+ ResultPoint.orderBestPatterns(patternInfo);
+
+ return new FinderPatternInfo(patternInfo);
+ }
+
+ /**
+ * Given a count of black/white/black/white/black pixels just seen and an end position,
+ * figures the location of the center of this run.
+ */
+ private static float centerFromEnd(int[] stateCount, int end) {
+ return (float) (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f;
+ }
+
+ /**
+ * @param stateCount count of black/white/black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
+ * used by finder patterns to be considered a match
+ */
+ protected static boolean foundPatternCross(int[] stateCount) {
+ int totalModuleSize = 0;
+ for (int i = 0; i < 5; i++) {
+ int count = stateCount[i];
+ if (count == 0) {
+ return false;
+ }
+ totalModuleSize += count;
+ }
+ if (totalModuleSize < 7) {
+ return false;
+ }
+ int moduleSize = (totalModuleSize << INTEGER_MATH_SHIFT) / 7;
+ int maxVariance = moduleSize / 2;
+ // Allow less than 50% variance from 1-1-3-1-1 proportions
+ return Math.abs(moduleSize - (stateCount[0] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(moduleSize - (stateCount[1] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(3 * moduleSize - (stateCount[2] << INTEGER_MATH_SHIFT)) < 3 * maxVariance &&
+ Math.abs(moduleSize - (stateCount[3] << INTEGER_MATH_SHIFT)) < maxVariance &&
+ Math.abs(moduleSize - (stateCount[4] << INTEGER_MATH_SHIFT)) < maxVariance;
+ }
+
+ private int[] getCrossCheckStateCount() {
+ crossCheckStateCount[0] = 0;
+ crossCheckStateCount[1] = 0;
+ crossCheckStateCount[2] = 0;
+ crossCheckStateCount[3] = 0;
+ crossCheckStateCount[4] = 0;
+ return crossCheckStateCount;
+ }
+
+ /**
+ * <p>After a horizontal scan finds a potential finder pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * finder pattern to see if the same proportion is detected.</p>
+ *
+ * @param startI row where a finder pattern was detected
+ * @param centerJ center of the section that appears to cross a finder pattern
+ * @param maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of finder pattern, or {@link Float#NaN} if not found
+ */
+ private float crossCheckVertical(int startI, int centerJ, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxI = image.getHeight();
+ int[] stateCount = getCrossCheckStateCount();
+
+ // Start counting up from center
+ int i = startI;
+ while (i >= 0 && image.get(centerJ, i)) {
+ stateCount[2]++;
+ i--;
+ }
+ if (i < 0) {
+ return Float.NaN;
+ }
+ while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if (i < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ i--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ // Now also count down from center
+ i = startI + 1;
+ while (i < maxI && image.get(centerJ, i)) {
+ stateCount[2]++;
+ i++;
+ }
+ if (i == maxI) {
+ return Float.NaN;
+ }
+ while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) {
+ stateCount[3]++;
+ i++;
+ }
+ if (i == maxI || stateCount[3] >= maxCount) {
+ return Float.NaN;
+ }
+ while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) {
+ stateCount[4]++;
+ i++;
+ }
+ if (stateCount[4] >= maxCount) {
+ return Float.NaN;
+ }
+
+ // If we found a finder-pattern-like section, but its size is more than 40% different than
+ // the original, assume it's a false positive
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;
+ }
+
+ /**
+ * <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
+ * except it reads horizontally instead of vertically. This is used to cross-cross
+ * check a vertical cross check and locate the real center of the alignment pattern.</p>
+ */
+ private float crossCheckHorizontal(int startJ, int centerI, int maxCount,
+ int originalStateCountTotal) {
+ BitMatrix image = this.image;
+
+ int maxJ = image.getWidth();
+ int[] stateCount = getCrossCheckStateCount();
+
+ int j = startJ;
+ while (j >= 0 && image.get(j, centerI)) {
+ stateCount[2]++;
+ j--;
+ }
+ if (j < 0) {
+ return Float.NaN;
+ }
+ while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) {
+ stateCount[1]++;
+ j--;
+ }
+ if (j < 0 || stateCount[1] > maxCount) {
+ return Float.NaN;
+ }
+ while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) {
+ stateCount[0]++;
+ j--;
+ }
+ if (stateCount[0] > maxCount) {
+ return Float.NaN;
+ }
+
+ j = startJ + 1;
+ while (j < maxJ && image.get(j, centerI)) {
+ stateCount[2]++;
+ j++;
+ }
+ if (j == maxJ) {
+ return Float.NaN;
+ }
+ while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) {
+ stateCount[3]++;
+ j++;
+ }
+ if (j == maxJ || stateCount[3] >= maxCount) {
+ return Float.NaN;
+ }
+ while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) {
+ stateCount[4]++;
+ j++;
+ }
+ if (stateCount[4] >= maxCount) {
+ return Float.NaN;
+ }
+
+ // If we found a finder-pattern-like section, but its size is significantly different than
+ // the original, assume it's a false positive
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) {
+ return Float.NaN;
+ }
+
+ return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN;
+ }
+
+ /**
+ * <p>This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will, ah, cross-cross-check
+ * with another horizontal scan. This is needed primarily to locate the real horizontal
+ * center of the pattern in cases of extreme skew.</p>
+ *
+ * <p>If that succeeds the finder pattern location is added to a list that tracks
+ * the number of times each location has been nearly-matched as a finder pattern.
+ * Each additional find is more evidence that the location is in fact a finder
+ * pattern center
+ *
+ * @param stateCount reading state module counts from horizontal scan
+ * @param i row where finder pattern may be found
+ * @param j end of possible finder pattern in row
+ * @return true if a finder pattern candidate was found this time
+ */
+ protected boolean handlePossibleCenter(int[] stateCount, int i, int j) {
+ int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] +
+ stateCount[4];
+ float centerJ = centerFromEnd(stateCount, j);
+ float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal);
+ if (!Float.isNaN(centerI)) {
+ // Re-cross check
+ centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal);
+ if (!Float.isNaN(centerJ)) {
+ float estimatedModuleSize = (float) stateCountTotal / 7.0f;
+ boolean found = false;
+ int max = possibleCenters.size();
+ for (int index = 0; index < max; index++) {
+ FinderPattern center = (FinderPattern) possibleCenters.elementAt(index);
+ // Look for about the same center and module size:
+ if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) {
+ center.incrementCount();
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
+ possibleCenters.addElement(point);
+ if (resultPointCallback != null) {
+ resultPointCallback.foundPossibleResultPoint(point);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return number of rows we could safely skip during scanning, based on the first
+ * two finder patterns that have been located. In some cases their position will
+ * allow us to infer that the third pattern must lie below a certain point farther
+ * down in the image.
+ */
+ private int findRowSkip() {
+ int max = possibleCenters.size();
+ if (max <= 1) {
+ return 0;
+ }
+ FinderPattern firstConfirmedCenter = null;
+ for (int i = 0; i < max; i++) {
+ FinderPattern center = (FinderPattern) possibleCenters.elementAt(i);
+ if (center.getCount() >= CENTER_QUORUM) {
+ if (firstConfirmedCenter == null) {
+ firstConfirmedCenter = center;
+ } else {
+ // We have two confirmed centers
+ // How far down can we skip before resuming looking for the next
+ // pattern? In the worst case, only the difference between the
+ // difference in the x / y coordinates of the two centers.
+ // This is the case where you find top left last.
+ hasSkipped = true;
+ return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) -
+ Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @return true iff we have found at least 3 finder patterns that have been detected
+ * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
+ * candidates is "pretty similar"
+ */
+ private boolean haveMultiplyConfirmedCenters() {
+ int confirmedCount = 0;
+ float totalModuleSize = 0.0f;
+ int max = possibleCenters.size();
+ for (int i = 0; i < max; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ if (pattern.getCount() >= CENTER_QUORUM) {
+ confirmedCount++;
+ totalModuleSize += pattern.getEstimatedModuleSize();
+ }
+ }
+ if (confirmedCount < 3) {
+ return false;
+ }
+ // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
+ // and that we need to keep looking. We detect this by asking if the estimated module sizes
+ // vary too much. We arbitrarily say that when the total deviation from average exceeds
+ // 5% of the total module size estimates, it's too much.
+ float average = totalModuleSize / (float) max;
+ float totalDeviation = 0.0f;
+ for (int i = 0; i < max; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average);
+ }
+ return totalDeviation <= 0.05f * totalModuleSize;
+ }
+
+ /**
+ * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
+ * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
+ * size differs from the average among those patterns the least
+ * @throws NotFoundException if 3 such finder patterns do not exist
+ */
+ private FinderPattern[] selectBestPatterns() throws NotFoundException {
+
+ int startSize = possibleCenters.size();
+ if (startSize < 3) {
+ // Couldn't find enough finder patterns
+ throw NotFoundException.getNotFoundInstance();
+ }
+
+ // Filter outlier possibilities whose module size is too different
+ if (startSize > 3) {
+ // But we can only afford to do so if we have at least 4 possibilities to choose from
+ float totalModuleSize = 0.0f;
+ float square = 0.0f;
+ for (int i = 0; i < startSize; i++) {
+ float size = ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
+ totalModuleSize += size;
+ square += size * size;
+ }
+ float average = totalModuleSize / (float) startSize;
+ float stdDev = (float) Math.sqrt(square / startSize - average * average);
+
+ Collections.insertionSort(possibleCenters, new FurthestFromAverageComparator(average));
+
+ float limit = Math.max(0.2f * average, stdDev);
+
+ for (int i = 0; i < possibleCenters.size() && possibleCenters.size() > 3; i++) {
+ FinderPattern pattern = (FinderPattern) possibleCenters.elementAt(i);
+ if (Math.abs(pattern.getEstimatedModuleSize() - average) > limit) {
+ possibleCenters.removeElementAt(i);
+ i--;
+ }
+ }
+ }
+
+ if (possibleCenters.size() > 3) {
+ // Throw away all but those first size candidate points we found.
+
+ float totalModuleSize = 0.0f;
+ for (int i = 0; i < possibleCenters.size(); i++) {
+ totalModuleSize += ((FinderPattern) possibleCenters.elementAt(i)).getEstimatedModuleSize();
+ }
+
+ float average = totalModuleSize / (float) possibleCenters.size();
+
+ Collections.insertionSort(possibleCenters, new CenterComparator(average));
+
+ possibleCenters.setSize(3);
+ }
+
+ return new FinderPattern[]{
+ (FinderPattern) possibleCenters.elementAt(0),
+ (FinderPattern) possibleCenters.elementAt(1),
+ (FinderPattern) possibleCenters.elementAt(2)
+ };
+ }
+
+ /**
+ * <p>Orders by furthest from average</p>
+ */
+ private static class FurthestFromAverageComparator implements Comparator {
+ private final float average;
+ private FurthestFromAverageComparator(float f) {
+ average = f;
+ }
+ public int compare(Object center1, Object center2) {
+ float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
+ float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
+ return dA < dB ? -1 : dA == dB ? 0 : 1;
+ }
+ }
+
+ /**
+ * <p>Orders by {@link FinderPattern#getCount()}, descending.</p>
+ */
+ private static class CenterComparator implements Comparator {
+ private final float average;
+ private CenterComparator(float f) {
+ average = f;
+ }
+ public int compare(Object center1, Object center2) {
+ if (((FinderPattern) center2).getCount() == ((FinderPattern) center1).getCount()) {
+ float dA = Math.abs(((FinderPattern) center2).getEstimatedModuleSize() - average);
+ float dB = Math.abs(((FinderPattern) center1).getEstimatedModuleSize() - average);
+ return dA < dB ? 1 : dA == dB ? 0 : -1;
+ } else {
+ return ((FinderPattern) center2).getCount() - ((FinderPattern) center1).getCount();
+ }
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
new file mode 100644
index 000000000..3c3401085
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.detector;
+
+/**
+ * <p>Encapsulates information about finder patterns in an image, including the location of
+ * the three finder patterns, and their estimated module size.</p>
+ *
+ * @author Sean Owen
+ */
+public final class FinderPatternInfo {
+
+ private final FinderPattern bottomLeft;
+ private final FinderPattern topLeft;
+ private final FinderPattern topRight;
+
+ public FinderPatternInfo(FinderPattern[] patternCenters) {
+ this.bottomLeft = patternCenters[0];
+ this.topLeft = patternCenters[1];
+ this.topRight = patternCenters[2];
+ }
+
+ public FinderPattern getBottomLeft() {
+ return bottomLeft;
+ }
+
+ public FinderPattern getTopLeft() {
+ return topLeft;
+ }
+
+ public FinderPattern getTopRight() {
+ return topRight;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
new file mode 100644
index 000000000..5714d9c3a
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+final class BlockPair {
+
+ private final byte[] dataBytes;
+ private final byte[] errorCorrectionBytes;
+
+ BlockPair(byte[] data, byte[] errorCorrection) {
+ dataBytes = data;
+ errorCorrectionBytes = errorCorrection;
+ }
+
+ public byte[] getDataBytes() {
+ return dataBytes;
+ }
+
+ public byte[] getErrorCorrectionBytes() {
+ return errorCorrectionBytes;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
new file mode 100644
index 000000000..eb248a26c
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+/**
+ * A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
+ * unsigned container, it's up to you to do byteValue & 0xff at each location.
+ *
+ * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
+ * -1, 0, and 1, I'm going to use less memory and go with bytes.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ByteMatrix {
+
+ private final byte[][] bytes;
+ private final int width;
+ private final int height;
+
+ public ByteMatrix(int width, int height) {
+ bytes = new byte[height][width];
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public byte get(int x, int y) {
+ return bytes[y][x];
+ }
+
+ public byte[][] getArray() {
+ return bytes;
+ }
+
+ public void set(int x, int y, byte value) {
+ bytes[y][x] = value;
+ }
+
+ public void set(int x, int y, int value) {
+ bytes[y][x] = (byte) value;
+ }
+
+ public void set(int x, int y, boolean value) {
+ bytes[y][x] = (byte) (value ? 1 : 0);
+ }
+
+ public void clear(byte value) {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ bytes[y][x] = value;
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(2 * width * height + 2);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ switch (bytes[y][x]) {
+ case 0:
+ result.append(" 0");
+ break;
+ case 1:
+ result.append(" 1");
+ break;
+ default:
+ result.append(" ");
+ break;
+ }
+ }
+ result.append('\n');
+ }
+ return result.toString();
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
new file mode 100644
index 000000000..8796511ab
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
@@ -0,0 +1,557 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitArray;
+import com.google.zxing.common.CharacterSetECI;
+import com.google.zxing.common.ECI;
+import com.google.zxing.common.reedsolomon.GenericGF;
+import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.decoder.Mode;
+import com.google.zxing.qrcode.decoder.Version;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class Encoder {
+
+ // The original table is defined in the table 5 of JISX0510:2004 (p.19).
+ private static final int[] ALPHANUMERIC_TABLE = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
+ 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
+ };
+
+ static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
+
+ private Encoder() {
+ }
+
+ // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
+ // Basically it applies four rules and summate all penalties.
+ private static int calculateMaskPenalty(ByteMatrix matrix) {
+ int penalty = 0;
+ penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
+ penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
+ return penalty;
+ }
+
+ /**
+ * Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
+ * internally by chooseMode(). On success, store the result in "qrCode".
+ *
+ * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
+ * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
+ * strong error correction for this purpose.
+ *
+ * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
+ * with which clients can specify the encoding mode. For now, we don't need the functionality.
+ */
+ public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
+ throws WriterException {
+ encode(content, ecLevel, null, qrCode);
+ }
+
+ public static void encode(String content, ErrorCorrectionLevel ecLevel, Hashtable hints,
+ QRCode qrCode) throws WriterException {
+
+ String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
+ if (encoding == null) {
+ encoding = DEFAULT_BYTE_MODE_ENCODING;
+ }
+
+ // Step 1: Choose the mode (encoding).
+ Mode mode = chooseMode(content, encoding);
+
+ // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
+ BitArray dataBits = new BitArray();
+ appendBytes(content, mode, dataBits, encoding);
+ // Step 3: Initialize QR code that can contain "dataBits".
+ int numInputBytes = dataBits.getSizeInBytes();
+ initQRCode(numInputBytes, ecLevel, mode, qrCode);
+
+ // Step 4: Build another bit vector that contains header and data.
+ BitArray headerAndDataBits = new BitArray();
+
+ // Step 4.5: Append ECI message if applicable
+ if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
+ CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
+ if (eci != null) {
+ appendECI(eci, headerAndDataBits);
+ }
+ }
+
+ appendModeInfo(mode, headerAndDataBits);
+
+ int numLetters = mode.equals(Mode.BYTE) ? dataBits.getSizeInBytes() : content.length();
+ appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
+ headerAndDataBits.appendBitArray(dataBits);
+
+ // Step 5: Terminate the bits properly.
+ terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
+
+ // Step 6: Interleave data bits with error correction code.
+ BitArray finalBits = new BitArray();
+ interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
+ qrCode.getNumRSBlocks(), finalBits);
+
+ // Step 7: Choose the mask pattern and set to "qrCode".
+ ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
+ qrCode.setMaskPattern(chooseMaskPattern(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
+ matrix));
+
+ // Step 8. Build the matrix and set it to "qrCode".
+ MatrixUtil.buildMatrix(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
+ qrCode.getMaskPattern(), matrix);
+ qrCode.setMatrix(matrix);
+ // Step 9. Make sure we have a valid QR Code.
+ if (!qrCode.isValid()) {
+ throw new WriterException("Invalid QR code: " + qrCode.toString());
+ }
+ }
+
+ /**
+ * @return the code point of the table used in alphanumeric mode or
+ * -1 if there is no corresponding code in the table.
+ */
+ static int getAlphanumericCode(int code) {
+ if (code < ALPHANUMERIC_TABLE.length) {
+ return ALPHANUMERIC_TABLE[code];
+ }
+ return -1;
+ }
+
+ public static Mode chooseMode(String content) {
+ return chooseMode(content, null);
+ }
+
+ /**
+ * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
+ * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
+ */
+ public static Mode chooseMode(String content, String encoding) {
+ if ("Shift_JIS".equals(encoding)) {
+ // Choose Kanji mode if all input are double-byte characters
+ return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE;
+ }
+ boolean hasNumeric = false;
+ boolean hasAlphanumeric = false;
+ for (int i = 0; i < content.length(); ++i) {
+ char c = content.charAt(i);
+ if (c >= '0' && c <= '9') {
+ hasNumeric = true;
+ } else if (getAlphanumericCode(c) != -1) {
+ hasAlphanumeric = true;
+ } else {
+ return Mode.BYTE;
+ }
+ }
+ if (hasAlphanumeric) {
+ return Mode.ALPHANUMERIC;
+ } else if (hasNumeric) {
+ return Mode.NUMERIC;
+ }
+ return Mode.BYTE;
+ }
+
+ private static boolean isOnlyDoubleByteKanji(String content) {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes("Shift_JIS");
+ } catch (UnsupportedEncodingException uee) {
+ return false;
+ }
+ int length = bytes.length;
+ if (length % 2 != 0) {
+ return false;
+ }
+ for (int i = 0; i < length; i += 2) {
+ int byte1 = bytes[i] & 0xFF;
+ if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static int chooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, int version,
+ ByteMatrix matrix) throws WriterException {
+
+ int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
+ int bestMaskPattern = -1;
+ // We try all mask patterns to choose the best one.
+ for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
+ MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
+ int penalty = calculateMaskPenalty(matrix);
+ if (penalty < minPenalty) {
+ minPenalty = penalty;
+ bestMaskPattern = maskPattern;
+ }
+ }
+ return bestMaskPattern;
+ }
+
+ /**
+ * Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success,
+ * modify "qrCode".
+ */
+ private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode,
+ QRCode qrCode) throws WriterException {
+ qrCode.setECLevel(ecLevel);
+ qrCode.setMode(mode);
+
+ // In the following comments, we use numbers of Version 7-H.
+ for (int versionNum = 1; versionNum <= 40; versionNum++) {
+ Version version = Version.getVersionForNumber(versionNum);
+ // numBytes = 196
+ int numBytes = version.getTotalCodewords();
+ // getNumECBytes = 130
+ Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
+ int numEcBytes = ecBlocks.getTotalECCodewords();
+ // getNumRSBlocks = 5
+ int numRSBlocks = ecBlocks.getNumBlocks();
+ // getNumDataBytes = 196 - 130 = 66
+ int numDataBytes = numBytes - numEcBytes;
+ // We want to choose the smallest version which can contain data of "numInputBytes" + some
+ // extra bits for the header (mode info and length info). The header can be three bytes
+ // (precisely 4 + 16 bits) at most. Hence we do +3 here.
+ if (numDataBytes >= numInputBytes + 3) {
+ // Yay, we found the proper rs block info!
+ qrCode.setVersion(versionNum);
+ qrCode.setNumTotalBytes(numBytes);
+ qrCode.setNumDataBytes(numDataBytes);
+ qrCode.setNumRSBlocks(numRSBlocks);
+ // getNumECBytes = 196 - 66 = 130
+ qrCode.setNumECBytes(numEcBytes);
+ // matrix width = 21 + 6 * 4 = 45
+ qrCode.setMatrixWidth(version.getDimensionForVersion());
+ return;
+ }
+ }
+ throw new WriterException("Cannot find proper rs block info (input data too big?)");
+ }
+
+ /**
+ * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
+ */
+ static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
+ int capacity = numDataBytes << 3;
+ if (bits.getSize() > capacity) {
+ throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
+ capacity);
+ }
+ for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
+ bits.appendBit(false);
+ }
+ // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
+ // If the last byte isn't 8-bit aligned, we'll add padding bits.
+ int numBitsInLastByte = bits.getSize() & 0x07;
+ if (numBitsInLastByte > 0) {
+ for (int i = numBitsInLastByte; i < 8; i++) {
+ bits.appendBit(false);
+ }
+ }
+ // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
+ int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
+ for (int i = 0; i < numPaddingBytes; ++i) {
+ bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
+ }
+ if (bits.getSize() != capacity) {
+ throw new WriterException("Bits size does not equal capacity");
+ }
+ }
+
+ /**
+ * Get number of data bytes and number of error correction bytes for block id "blockID". Store
+ * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
+ * JISX0510:2004 (p.30)
+ */
+ static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes,
+ int numRSBlocks, int blockID, int[] numDataBytesInBlock,
+ int[] numECBytesInBlock) throws WriterException {
+ if (blockID >= numRSBlocks) {
+ throw new WriterException("Block ID too large");
+ }
+ // numRsBlocksInGroup2 = 196 % 5 = 1
+ int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
+ // numRsBlocksInGroup1 = 5 - 1 = 4
+ int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
+ // numTotalBytesInGroup1 = 196 / 5 = 39
+ int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
+ // numTotalBytesInGroup2 = 39 + 1 = 40
+ int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
+ // numDataBytesInGroup1 = 66 / 5 = 13
+ int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
+ // numDataBytesInGroup2 = 13 + 1 = 14
+ int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
+ // numEcBytesInGroup1 = 39 - 13 = 26
+ int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
+ // numEcBytesInGroup2 = 40 - 14 = 26
+ int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
+ // Sanity checks.
+ // 26 = 26
+ if (numEcBytesInGroup1 != numEcBytesInGroup2) {
+ throw new WriterException("EC bytes mismatch");
+ }
+ // 5 = 4 + 1.
+ if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
+ throw new WriterException("RS blocks mismatch");
+ }
+ // 196 = (13 + 26) * 4 + (14 + 26) * 1
+ if (numTotalBytes !=
+ ((numDataBytesInGroup1 + numEcBytesInGroup1) *
+ numRsBlocksInGroup1) +
+ ((numDataBytesInGroup2 + numEcBytesInGroup2) *
+ numRsBlocksInGroup2)) {
+ throw new WriterException("Total bytes mismatch");
+ }
+
+ if (blockID < numRsBlocksInGroup1) {
+ numDataBytesInBlock[0] = numDataBytesInGroup1;
+ numECBytesInBlock[0] = numEcBytesInGroup1;
+ } else {
+ numDataBytesInBlock[0] = numDataBytesInGroup2;
+ numECBytesInBlock[0] = numEcBytesInGroup2;
+ }
+ }
+
+ /**
+ * Interleave "bits" with corresponding error correction bytes. On success, store the result in
+ * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
+ */
+ static void interleaveWithECBytes(BitArray bits, int numTotalBytes,
+ int numDataBytes, int numRSBlocks, BitArray result) throws WriterException {
+
+ // "bits" must have "getNumDataBytes" bytes of data.
+ if (bits.getSizeInBytes() != numDataBytes) {
+ throw new WriterException("Number of bits and data bytes does not match");
+ }
+
+ // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
+ // store the divided data bytes blocks and error correction bytes blocks into "blocks".
+ int dataBytesOffset = 0;
+ int maxNumDataBytes = 0;
+ int maxNumEcBytes = 0;
+
+ // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
+ Vector blocks = new Vector(numRSBlocks);
+
+ for (int i = 0; i < numRSBlocks; ++i) {
+ int[] numDataBytesInBlock = new int[1];
+ int[] numEcBytesInBlock = new int[1];
+ getNumDataBytesAndNumECBytesForBlockID(
+ numTotalBytes, numDataBytes, numRSBlocks, i,
+ numDataBytesInBlock, numEcBytesInBlock);
+
+ int size = numDataBytesInBlock[0];
+ byte[] dataBytes = new byte[size];
+ bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
+ byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
+ blocks.addElement(new BlockPair(dataBytes, ecBytes));
+
+ maxNumDataBytes = Math.max(maxNumDataBytes, size);
+ maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);
+ dataBytesOffset += numDataBytesInBlock[0];
+ }
+ if (numDataBytes != dataBytesOffset) {
+ throw new WriterException("Data bytes does not match offset");
+ }
+
+ // First, place data blocks.
+ for (int i = 0; i < maxNumDataBytes; ++i) {
+ for (int j = 0; j < blocks.size(); ++j) {
+ byte[] dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes();
+ if (i < dataBytes.length) {
+ result.appendBits(dataBytes[i], 8);
+ }
+ }
+ }
+ // Then, place error correction blocks.
+ for (int i = 0; i < maxNumEcBytes; ++i) {
+ for (int j = 0; j < blocks.size(); ++j) {
+ byte[] ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes();
+ if (i < ecBytes.length) {
+ result.appendBits(ecBytes[i], 8);
+ }
+ }
+ }
+ if (numTotalBytes != result.getSizeInBytes()) { // Should be same.
+ throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
+ result.getSizeInBytes() + " differ.");
+ }
+ }
+
+ static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
+ int numDataBytes = dataBytes.length;
+ int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
+ for (int i = 0; i < numDataBytes; i++) {
+ toEncode[i] = dataBytes[i] & 0xFF;
+ }
+ new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
+
+ byte[] ecBytes = new byte[numEcBytesInBlock];
+ for (int i = 0; i < numEcBytesInBlock; i++) {
+ ecBytes[i] = (byte) toEncode[numDataBytes + i];
+ }
+ return ecBytes;
+ }
+
+ /**
+ * Append mode info. On success, store the result in "bits".
+ */
+ static void appendModeInfo(Mode mode, BitArray bits) {
+ bits.appendBits(mode.getBits(), 4);
+ }
+
+
+ /**
+ * Append length info. On success, store the result in "bits".
+ */
+ static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits)
+ throws WriterException {
+ int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
+ if (numLetters > ((1 << numBits) - 1)) {
+ throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
+ }
+ bits.appendBits(numLetters, numBits);
+ }
+
+ /**
+ * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
+ */
+ static void appendBytes(String content, Mode mode, BitArray bits, String encoding)
+ throws WriterException {
+ if (mode.equals(Mode.NUMERIC)) {
+ appendNumericBytes(content, bits);
+ } else if (mode.equals(Mode.ALPHANUMERIC)) {
+ appendAlphanumericBytes(content, bits);
+ } else if (mode.equals(Mode.BYTE)) {
+ append8BitBytes(content, bits, encoding);
+ } else if (mode.equals(Mode.KANJI)) {
+ appendKanjiBytes(content, bits);
+ } else {
+ throw new WriterException("Invalid mode: " + mode);
+ }
+ }
+
+ static void appendNumericBytes(String content, BitArray bits) {
+ int length = content.length();
+ int i = 0;
+ while (i < length) {
+ int num1 = content.charAt(i) - '0';
+ if (i + 2 < length) {
+ // Encode three numeric letters in ten bits.
+ int num2 = content.charAt(i + 1) - '0';
+ int num3 = content.charAt(i + 2) - '0';
+ bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
+ i += 3;
+ } else if (i + 1 < length) {
+ // Encode two numeric letters in seven bits.
+ int num2 = content.charAt(i + 1) - '0';
+ bits.appendBits(num1 * 10 + num2, 7);
+ i += 2;
+ } else {
+ // Encode one numeric letter in four bits.
+ bits.appendBits(num1, 4);
+ i++;
+ }
+ }
+ }
+
+ static void appendAlphanumericBytes(String content, BitArray bits) throws WriterException {
+ int length = content.length();
+ int i = 0;
+ while (i < length) {
+ int code1 = getAlphanumericCode(content.charAt(i));
+ if (code1 == -1) {
+ throw new WriterException();
+ }
+ if (i + 1 < length) {
+ int code2 = getAlphanumericCode(content.charAt(i + 1));
+ if (code2 == -1) {
+ throw new WriterException();
+ }
+ // Encode two alphanumeric letters in 11 bits.
+ bits.appendBits(code1 * 45 + code2, 11);
+ i += 2;
+ } else {
+ // Encode one alphanumeric letter in six bits.
+ bits.appendBits(code1, 6);
+ i++;
+ }
+ }
+ }
+
+ static void append8BitBytes(String content, BitArray bits, String encoding)
+ throws WriterException {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes(encoding);
+ } catch (UnsupportedEncodingException uee) {
+ throw new WriterException(uee.toString());
+ }
+ for (int i = 0; i < bytes.length; ++i) {
+ bits.appendBits(bytes[i], 8);
+ }
+ }
+
+ static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
+ byte[] bytes;
+ try {
+ bytes = content.getBytes("Shift_JIS");
+ } catch (UnsupportedEncodingException uee) {
+ throw new WriterException(uee.toString());
+ }
+ int length = bytes.length;
+ for (int i = 0; i < length; i += 2) {
+ int byte1 = bytes[i] & 0xFF;
+ int byte2 = bytes[i + 1] & 0xFF;
+ int code = (byte1 << 8) | byte2;
+ int subtracted = -1;
+ if (code >= 0x8140 && code <= 0x9ffc) {
+ subtracted = code - 0x8140;
+ } else if (code >= 0xe040 && code <= 0xebbf) {
+ subtracted = code - 0xc140;
+ }
+ if (subtracted == -1) {
+ throw new WriterException("Invalid byte sequence");
+ }
+ int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
+ bits.appendBits(encoded, 13);
+ }
+ }
+
+ private static void appendECI(ECI eci, BitArray bits) {
+ bits.appendBits(Mode.ECI.getBits(), 4);
+ // This is correct for values up to 127, which is all we need now.
+ bits.appendBits(eci.getValue(), 8);
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
new file mode 100644
index 000000000..61ccf48c1
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class MaskUtil {
+
+ private MaskUtil() {
+ // do nothing
+ }
+
+ // Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
+ // give penalty to them. Example: 00000 or 11111.
+ public static int applyMaskPenaltyRule1(ByteMatrix matrix) {
+ return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
+ }
+
+ // Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
+ // penalty to them.
+ public static int applyMaskPenaltyRule2(ByteMatrix matrix) {
+ int penalty = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height - 1; ++y) {
+ for (int x = 0; x < width - 1; ++x) {
+ int value = array[y][x];
+ if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
+ penalty += 3;
+ }
+ }
+ }
+ return penalty;
+ }
+
+ // Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
+ // 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
+ // penalties twice (i.e. 40 * 2).
+ public static int applyMaskPenaltyRule3(ByteMatrix matrix) {
+ int penalty = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ // Tried to simplify following conditions but failed.
+ if (x + 6 < width &&
+ array[y][x] == 1 &&
+ array[y][x + 1] == 0 &&
+ array[y][x + 2] == 1 &&
+ array[y][x + 3] == 1 &&
+ array[y][x + 4] == 1 &&
+ array[y][x + 5] == 0 &&
+ array[y][x + 6] == 1 &&
+ ((x + 10 < width &&
+ array[y][x + 7] == 0 &&
+ array[y][x + 8] == 0 &&
+ array[y][x + 9] == 0 &&
+ array[y][x + 10] == 0) ||
+ (x - 4 >= 0 &&
+ array[y][x - 1] == 0 &&
+ array[y][x - 2] == 0 &&
+ array[y][x - 3] == 0 &&
+ array[y][x - 4] == 0))) {
+ penalty += 40;
+ }
+ if (y + 6 < height &&
+ array[y][x] == 1 &&
+ array[y + 1][x] == 0 &&
+ array[y + 2][x] == 1 &&
+ array[y + 3][x] == 1 &&
+ array[y + 4][x] == 1 &&
+ array[y + 5][x] == 0 &&
+ array[y + 6][x] == 1 &&
+ ((y + 10 < height &&
+ array[y + 7][x] == 0 &&
+ array[y + 8][x] == 0 &&
+ array[y + 9][x] == 0 &&
+ array[y + 10][x] == 0) ||
+ (y - 4 >= 0 &&
+ array[y - 1][x] == 0 &&
+ array[y - 2][x] == 0 &&
+ array[y - 3][x] == 0 &&
+ array[y - 4][x] == 0))) {
+ penalty += 40;
+ }
+ }
+ }
+ return penalty;
+ }
+
+ // Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
+ // penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
+ // - 0% => 100
+ // - 40% => 20
+ // - 45% => 10
+ // - 50% => 0
+ // - 55% => 10
+ // - 55% => 20
+ // - 100% => 100
+ public static int applyMaskPenaltyRule4(ByteMatrix matrix) {
+ int numDarkCells = 0;
+ byte[][] array = matrix.getArray();
+ int width = matrix.getWidth();
+ int height = matrix.getHeight();
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (array[y][x] == 1) {
+ numDarkCells += 1;
+ }
+ }
+ }
+ int numTotalCells = matrix.getHeight() * matrix.getWidth();
+ double darkRatio = (double) numDarkCells / numTotalCells;
+ return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10;
+ }
+
+ // Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
+ // pattern conditions.
+ public static boolean getDataMaskBit(int maskPattern, int x, int y) {
+ if (!QRCode.isValidMaskPattern(maskPattern)) {
+ throw new IllegalArgumentException("Invalid mask pattern");
+ }
+ int intermediate;
+ int temp;
+ switch (maskPattern) {
+ case 0:
+ intermediate = (y + x) & 0x1;
+ break;
+ case 1:
+ intermediate = y & 0x1;
+ break;
+ case 2:
+ intermediate = x % 3;
+ break;
+ case 3:
+ intermediate = (y + x) % 3;
+ break;
+ case 4:
+ intermediate = ((y >>> 1) + (x / 3)) & 0x1;
+ break;
+ case 5:
+ temp = y * x;
+ intermediate = (temp & 0x1) + (temp % 3);
+ break;
+ case 6:
+ temp = y * x;
+ intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;
+ break;
+ case 7:
+ temp = y * x;
+ intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
+ }
+ return intermediate == 0;
+ }
+
+ // Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
+ // vertical and horizontal orders respectively.
+ private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
+ int penalty = 0;
+ int numSameBitCells = 0;
+ int prevBit = -1;
+ // Horizontal mode:
+ // for (int i = 0; i < matrix.height(); ++i) {
+ // for (int j = 0; j < matrix.width(); ++j) {
+ // int bit = matrix.get(i, j);
+ // Vertical mode:
+ // for (int i = 0; i < matrix.width(); ++i) {
+ // for (int j = 0; j < matrix.height(); ++j) {
+ // int bit = matrix.get(j, i);
+ int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
+ int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
+ byte[][] array = matrix.getArray();
+ for (int i = 0; i < iLimit; ++i) {
+ for (int j = 0; j < jLimit; ++j) {
+ int bit = isHorizontal ? array[i][j] : array[j][i];
+ if (bit == prevBit) {
+ numSameBitCells += 1;
+ // Found five repetitive cells with the same color (bit).
+ // We'll give penalty of 3.
+ if (numSameBitCells == 5) {
+ penalty += 3;
+ } else if (numSameBitCells > 5) {
+ // After five repetitive cells, we'll add the penalty one
+ // by one.
+ penalty += 1;
+ }
+ } else {
+ numSameBitCells = 1; // Include the cell itself.
+ prevBit = bit;
+ }
+ }
+ numSameBitCells = 0; // Clear at each row/column.
+ }
+ return penalty;
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
new file mode 100644
index 000000000..3d434e675
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitArray;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class MatrixUtil {
+
+ private MatrixUtil() {
+ // do nothing
+ }
+
+ private static final int[][] POSITION_DETECTION_PATTERN = {
+ {1, 1, 1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 1, 1, 1, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1},
+ {1, 1, 1, 1, 1, 1, 1},
+ };
+
+ private static final int[][] HORIZONTAL_SEPARATION_PATTERN = {
+ {0, 0, 0, 0, 0, 0, 0, 0},
+ };
+
+ private static final int[][] VERTICAL_SEPARATION_PATTERN = {
+ {0}, {0}, {0}, {0}, {0}, {0}, {0},
+ };
+
+ private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
+ {1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 1},
+ {1, 0, 1, 0, 1},
+ {1, 0, 0, 0, 1},
+ {1, 1, 1, 1, 1},
+ };
+
+ // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
+ private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {
+ {-1, -1, -1, -1, -1, -1, -1}, // Version 1
+ { 6, 18, -1, -1, -1, -1, -1}, // Version 2
+ { 6, 22, -1, -1, -1, -1, -1}, // Version 3
+ { 6, 26, -1, -1, -1, -1, -1}, // Version 4
+ { 6, 30, -1, -1, -1, -1, -1}, // Version 5
+ { 6, 34, -1, -1, -1, -1, -1}, // Version 6
+ { 6, 22, 38, -1, -1, -1, -1}, // Version 7
+ { 6, 24, 42, -1, -1, -1, -1}, // Version 8
+ { 6, 26, 46, -1, -1, -1, -1}, // Version 9
+ { 6, 28, 50, -1, -1, -1, -1}, // Version 10
+ { 6, 30, 54, -1, -1, -1, -1}, // Version 11
+ { 6, 32, 58, -1, -1, -1, -1}, // Version 12
+ { 6, 34, 62, -1, -1, -1, -1}, // Version 13
+ { 6, 26, 46, 66, -1, -1, -1}, // Version 14
+ { 6, 26, 48, 70, -1, -1, -1}, // Version 15
+ { 6, 26, 50, 74, -1, -1, -1}, // Version 16
+ { 6, 30, 54, 78, -1, -1, -1}, // Version 17
+ { 6, 30, 56, 82, -1, -1, -1}, // Version 18
+ { 6, 30, 58, 86, -1, -1, -1}, // Version 19
+ { 6, 34, 62, 90, -1, -1, -1}, // Version 20
+ { 6, 28, 50, 72, 94, -1, -1}, // Version 21
+ { 6, 26, 50, 74, 98, -1, -1}, // Version 22
+ { 6, 30, 54, 78, 102, -1, -1}, // Version 23
+ { 6, 28, 54, 80, 106, -1, -1}, // Version 24
+ { 6, 32, 58, 84, 110, -1, -1}, // Version 25
+ { 6, 30, 58, 86, 114, -1, -1}, // Version 26
+ { 6, 34, 62, 90, 118, -1, -1}, // Version 27
+ { 6, 26, 50, 74, 98, 122, -1}, // Version 28
+ { 6, 30, 54, 78, 102, 126, -1}, // Version 29
+ { 6, 26, 52, 78, 104, 130, -1}, // Version 30
+ { 6, 30, 56, 82, 108, 134, -1}, // Version 31
+ { 6, 34, 60, 86, 112, 138, -1}, // Version 32
+ { 6, 30, 58, 86, 114, 142, -1}, // Version 33
+ { 6, 34, 62, 90, 118, 146, -1}, // Version 34
+ { 6, 30, 54, 78, 102, 126, 150}, // Version 35
+ { 6, 24, 50, 76, 102, 128, 154}, // Version 36
+ { 6, 28, 54, 80, 106, 132, 158}, // Version 37
+ { 6, 32, 58, 84, 110, 136, 162}, // Version 38
+ { 6, 26, 54, 82, 110, 138, 166}, // Version 39
+ { 6, 30, 58, 86, 114, 142, 170}, // Version 40
+ };
+
+ // Type info cells at the left top corner.
+ private static final int[][] TYPE_INFO_COORDINATES = {
+ {8, 0},
+ {8, 1},
+ {8, 2},
+ {8, 3},
+ {8, 4},
+ {8, 5},
+ {8, 7},
+ {8, 8},
+ {7, 8},
+ {5, 8},
+ {4, 8},
+ {3, 8},
+ {2, 8},
+ {1, 8},
+ {0, 8},
+ };
+
+ // From Appendix D in JISX0510:2004 (p. 67)
+ private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
+
+ // From Appendix C in JISX0510:2004 (p.65).
+ private static final int TYPE_INFO_POLY = 0x537;
+ private static final int TYPE_INFO_MASK_PATTERN = 0x5412;
+
+ // Set all cells to -1. -1 means that the cell is empty (not set yet).
+ //
+ // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
+ // with the ByteMatrix initialized all to zero.
+ public static void clearMatrix(ByteMatrix matrix) {
+ matrix.clear((byte) -1);
+ }
+
+ // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
+ // success, store the result in "matrix" and return true.
+ public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, int version,
+ int maskPattern, ByteMatrix matrix) throws WriterException {
+ clearMatrix(matrix);
+ embedBasicPatterns(version, matrix);
+ // Type information appear with any version.
+ embedTypeInfo(ecLevel, maskPattern, matrix);
+ // Version info appear if version >= 7.
+ maybeEmbedVersionInfo(version, matrix);
+ // Data should be embedded at end.
+ embedDataBits(dataBits, maskPattern, matrix);
+ }
+
+ // Embed basic patterns. On success, modify the matrix and return true.
+ // The basic patterns are:
+ // - Position detection patterns
+ // - Timing patterns
+ // - Dark dot at the left bottom corner
+ // - Position adjustment patterns, if need be
+ public static void embedBasicPatterns(int version, ByteMatrix matrix) throws WriterException {
+ // Let's get started with embedding big squares at corners.
+ embedPositionDetectionPatternsAndSeparators(matrix);
+ // Then, embed the dark dot at the left bottom corner.
+ embedDarkDotAtLeftBottomCorner(matrix);
+
+ // Position adjustment patterns appear if version >= 2.
+ maybeEmbedPositionAdjustmentPatterns(version, matrix);
+ // Timing patterns should be embedded after position adj. patterns.
+ embedTimingPatterns(matrix);
+ }
+
+ // Embed type information. On success, modify the matrix.
+ public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
+ throws WriterException {
+ BitArray typeInfoBits = new BitArray();
+ makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
+
+ for (int i = 0; i < typeInfoBits.getSize(); ++i) {
+ // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
+ // "typeInfoBits".
+ boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
+
+ // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
+ int x1 = TYPE_INFO_COORDINATES[i][0];
+ int y1 = TYPE_INFO_COORDINATES[i][1];
+ matrix.set(x1, y1, bit);
+
+ if (i < 8) {
+ // Right top corner.
+ int x2 = matrix.getWidth() - i - 1;
+ int y2 = 8;
+ matrix.set(x2, y2, bit);
+ } else {
+ // Left bottom corner.
+ int x2 = 8;
+ int y2 = matrix.getHeight() - 7 + (i - 8);
+ matrix.set(x2, y2, bit);
+ }
+ }
+ }
+
+ // Embed version information if need be. On success, modify the matrix and return true.
+ // See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
+ public static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
+ if (version < 7) { // Version info is necessary if version >= 7.
+ return; // Don't need version info.
+ }
+ BitArray versionInfoBits = new BitArray();
+ makeVersionInfoBits(version, versionInfoBits);
+
+ int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ // Place bits in LSB (least significant bit) to MSB order.
+ boolean bit = versionInfoBits.get(bitIndex);
+ bitIndex--;
+ // Left bottom corner.
+ matrix.set(i, matrix.getHeight() - 11 + j, bit);
+ // Right bottom corner.
+ matrix.set(matrix.getHeight() - 11 + j, i, bit);
+ }
+ }
+ }
+
+ // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
+ // For debugging purposes, it skips masking process if "getMaskPattern" is -1.
+ // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
+ public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
+ throws WriterException {
+ int bitIndex = 0;
+ int direction = -1;
+ // Start from the right bottom cell.
+ int x = matrix.getWidth() - 1;
+ int y = matrix.getHeight() - 1;
+ while (x > 0) {
+ // Skip the vertical timing pattern.
+ if (x == 6) {
+ x -= 1;
+ }
+ while (y >= 0 && y < matrix.getHeight()) {
+ for (int i = 0; i < 2; ++i) {
+ int xx = x - i;
+ // Skip the cell if it's not empty.
+ if (!isEmpty(matrix.get(xx, y))) {
+ continue;
+ }
+ boolean bit;
+ if (bitIndex < dataBits.getSize()) {
+ bit = dataBits.get(bitIndex);
+ ++bitIndex;
+ } else {
+ // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
+ // in 8.4.9 of JISX0510:2004 (p. 24).
+ bit = false;
+ }
+
+ // Skip masking if mask_pattern is -1.
+ if (maskPattern != -1) {
+ if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
+ bit = !bit;
+ }
+ }
+ matrix.set(xx, y, bit);
+ }
+ y += direction;
+ }
+ direction = -direction; // Reverse the direction.
+ y += direction;
+ x -= 2; // Move to the left.
+ }
+ // All bits should be consumed.
+ if (bitIndex != dataBits.getSize()) {
+ throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
+ }
+ }
+
+ // Return the position of the most significant bit set (to one) in the "value". The most
+ // significant bit is position 32. If there is no bit set, return 0. Examples:
+ // - findMSBSet(0) => 0
+ // - findMSBSet(1) => 1
+ // - findMSBSet(255) => 8
+ public static int findMSBSet(int value) {
+ int numDigits = 0;
+ while (value != 0) {
+ value >>>= 1;
+ ++numDigits;
+ }
+ return numDigits;
+ }
+
+ // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
+ // code is used for encoding type information and version information.
+ // Example: Calculation of version information of 7.
+ // f(x) is created from 7.
+ // - 7 = 000111 in 6 bits
+ // - f(x) = x^2 + x^1 + x^0
+ // g(x) is given by the standard (p. 67)
+ // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
+ // Multiply f(x) by x^(18 - 6)
+ // - f'(x) = f(x) * x^(18 - 6)
+ // - f'(x) = x^14 + x^13 + x^12
+ // Calculate the remainder of f'(x) / g(x)
+ // x^2
+ // __________________________________________________
+ // g(x) )x^14 + x^13 + x^12
+ // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
+ // --------------------------------------------------
+ // x^11 + x^10 + x^7 + x^4 + x^2
+ //
+ // The remainder is x^11 + x^10 + x^7 + x^4 + x^2
+ // Encode it in binary: 110010010100
+ // The return value is 0xc94 (1100 1001 0100)
+ //
+ // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
+ // operations. We don't care if cofficients are positive or negative.
+ public static int calculateBCHCode(int value, int poly) {
+ // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
+ // from 13 to make it 12.
+ int msbSetInPoly = findMSBSet(poly);
+ value <<= msbSetInPoly - 1;
+ // Do the division business using exclusive-or operations.
+ while (findMSBSet(value) >= msbSetInPoly) {
+ value ^= poly << (findMSBSet(value) - msbSetInPoly);
+ }
+ // Now the "value" is the remainder (i.e. the BCH code)
+ return value;
+ }
+
+ // Make bit vector of type information. On success, store the result in "bits" and return true.
+ // Encode error correction level and mask pattern. See 8.9 of
+ // JISX0510:2004 (p.45) for details.
+ public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
+ throws WriterException {
+ if (!QRCode.isValidMaskPattern(maskPattern)) {
+ throw new WriterException("Invalid mask pattern");
+ }
+ int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
+ bits.appendBits(typeInfo, 5);
+
+ int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
+ bits.appendBits(bchCode, 10);
+
+ BitArray maskBits = new BitArray();
+ maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
+ bits.xor(maskBits);
+
+ if (bits.getSize() != 15) { // Just in case.
+ throw new WriterException("should not happen but we got: " + bits.getSize());
+ }
+ }
+
+ // Make bit vector of version information. On success, store the result in "bits" and return true.
+ // See 8.10 of JISX0510:2004 (p.45) for details.
+ public static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
+ bits.appendBits(version, 6);
+ int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
+ bits.appendBits(bchCode, 12);
+
+ if (bits.getSize() != 18) { // Just in case.
+ throw new WriterException("should not happen but we got: " + bits.getSize());
+ }
+ }
+
+ // Check if "value" is empty.
+ private static boolean isEmpty(int value) {
+ return value == -1;
+ }
+
+ // Check if "value" is valid.
+ private static boolean isValidValue(int value) {
+ return value == -1 || // Empty.
+ value == 0 || // Light (white).
+ value == 1; // Dark (black).
+ }
+
+ private static void embedTimingPatterns(ByteMatrix matrix) throws WriterException {
+ // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
+ // separation patterns (size 1). Thus, 8 = 7 + 1.
+ for (int i = 8; i < matrix.getWidth() - 8; ++i) {
+ int bit = (i + 1) % 2;
+ // Horizontal line.
+ if (!isValidValue(matrix.get(i, 6))) {
+ throw new WriterException();
+ }
+ if (isEmpty(matrix.get(i, 6))) {
+ matrix.set(i, 6, bit);
+ }
+ // Vertical line.
+ if (!isValidValue(matrix.get(6, i))) {
+ throw new WriterException();
+ }
+ if (isEmpty(matrix.get(6, i))) {
+ matrix.set(6, i, bit);
+ }
+ }
+ }
+
+ // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
+ private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
+ if (matrix.get(8, matrix.getHeight() - 8) == 0) {
+ throw new WriterException();
+ }
+ matrix.set(8, matrix.getHeight() - 8, 1);
+ }
+
+ private static void embedHorizontalSeparationPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (HORIZONTAL_SEPARATION_PATTERN[0].length != 8 || HORIZONTAL_SEPARATION_PATTERN.length != 1) {
+ throw new WriterException("Bad horizontal separation pattern");
+ }
+ for (int x = 0; x < 8; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
+ }
+ }
+
+ private static void embedVerticalSeparationPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (VERTICAL_SEPARATION_PATTERN[0].length != 1 || VERTICAL_SEPARATION_PATTERN.length != 7) {
+ throw new WriterException("Bad vertical separation pattern");
+ }
+ for (int y = 0; y < 7; ++y) {
+ if (!isEmpty(matrix.get(xStart, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
+ }
+ }
+
+ // Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
+ // almost identical, since we cannot write a function that takes 2D arrays in different sizes in
+ // C/C++. We should live with the fact.
+ private static void embedPositionAdjustmentPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (POSITION_ADJUSTMENT_PATTERN[0].length != 5 || POSITION_ADJUSTMENT_PATTERN.length != 5) {
+ throw new WriterException("Bad position adjustment");
+ }
+ for (int y = 0; y < 5; ++y) {
+ for (int x = 0; x < 5; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
+ }
+ }
+ }
+
+ private static void embedPositionDetectionPattern(int xStart, int yStart,
+ ByteMatrix matrix) throws WriterException {
+ // We know the width and height.
+ if (POSITION_DETECTION_PATTERN[0].length != 7 || POSITION_DETECTION_PATTERN.length != 7) {
+ throw new WriterException("Bad position detection pattern");
+ }
+ for (int y = 0; y < 7; ++y) {
+ for (int x = 0; x < 7; ++x) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
+ throw new WriterException();
+ }
+ matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
+ }
+ }
+ }
+
+ // Embed position detection patterns and surrounding vertical/horizontal separators.
+ private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
+ // Embed three big squares at corners.
+ int pdpWidth = POSITION_DETECTION_PATTERN[0].length;
+ // Left top corner.
+ embedPositionDetectionPattern(0, 0, matrix);
+ // Right top corner.
+ embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
+ // Left bottom corner.
+ embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
+
+ // Embed horizontal separation patterns around the squares.
+ int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length;
+ // Left top corner.
+ embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
+ // Right top corner.
+ embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
+ hspWidth - 1, matrix);
+ // Left bottom corner.
+ embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
+
+ // Embed vertical separation patterns around the squares.
+ int vspSize = VERTICAL_SEPARATION_PATTERN.length;
+ // Left top corner.
+ embedVerticalSeparationPattern(vspSize, 0, matrix);
+ // Right top corner.
+ embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
+ // Left bottom corner.
+ embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
+ matrix);
+ }
+
+ // Embed position adjustment patterns if need be.
+ private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)
+ throws WriterException {
+ if (version < 2) { // The patterns appear if version >= 2
+ return;
+ }
+ int index = version - 1;
+ int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
+ int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
+ for (int i = 0; i < numCoordinates; ++i) {
+ for (int j = 0; j < numCoordinates; ++j) {
+ int y = coordinates[i];
+ int x = coordinates[j];
+ if (x == -1 || y == -1) {
+ continue;
+ }
+ // If the cell is unset, we embed the position adjustment pattern here.
+ if (isEmpty(matrix.get(x, y))) {
+ // -2 is necessary since the x/y coordinates point to the center of the pattern, not the
+ // left top corner.
+ embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
+ }
+ }
+ }
+ }
+
+}
diff --git a/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
new file mode 100644
index 000000000..05c818513
--- /dev/null
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.qrcode.encoder;
+
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import com.google.zxing.qrcode.decoder.Mode;
+
+/**
+ * @author satorux@google.com (Satoru Takabayashi) - creator
+ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
+ */
+public final class QRCode {
+
+ public static final int NUM_MASK_PATTERNS = 8;
+
+ private Mode mode;
+ private ErrorCorrectionLevel ecLevel;
+ private int version;
+ private int matrixWidth;
+ private int maskPattern;
+ private int numTotalBytes;
+ private int numDataBytes;
+ private int numECBytes;
+ private int numRSBlocks;
+ private ByteMatrix matrix;
+
+ public QRCode() {
+ mode = null;
+ ecLevel = null;
+ version = -1;
+ matrixWidth = -1;
+ maskPattern = -1;
+ numTotalBytes = -1;
+ numDataBytes = -1;
+ numECBytes = -1;
+ numRSBlocks = -1;
+ matrix = null;
+ }
+
+ // Mode of the QR Code.
+ public Mode getMode() {
+ return mode;
+ }
+
+ // Error correction level of the QR Code.
+ public ErrorCorrectionLevel getECLevel() {
+ return ecLevel;
+ }
+
+ // Version of the QR Code. The bigger size, the bigger version.
+ public int getVersion() {
+ return version;
+ }
+
+ // ByteMatrix width of the QR Code.
+ public int getMatrixWidth() {
+ return matrixWidth;
+ }
+
+ // Mask pattern of the QR Code.
+ public int getMaskPattern() {
+ return maskPattern;
+ }
+
+ // Number of total bytes in the QR Code.
+ public int getNumTotalBytes() {
+ return numTotalBytes;
+ }
+
+ // Number of data bytes in the QR Code.
+ public int getNumDataBytes() {
+ return numDataBytes;
+ }
+
+ // Number of error correction bytes in the QR Code.
+ public int getNumECBytes() {
+ return numECBytes;
+ }
+
+ // Number of Reedsolomon blocks in the QR Code.
+ public int getNumRSBlocks() {
+ return numRSBlocks;
+ }
+
+ // ByteMatrix data of the QR Code.
+ public ByteMatrix getMatrix() {
+ return matrix;
+ }
+
+
+ // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
+ // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
+ public int at(int x, int y) {
+ // The value must be zero or one.
+ int value = matrix.get(x, y);
+ if (!(value == 0 || value == 1)) {
+ // this is really like an assert... not sure what better exception to use?
+ throw new RuntimeException("Bad value");
+ }
+ return value;
+ }
+
+ // Checks all the member variables are set properly. Returns true on success. Otherwise, returns
+ // false.
+ public boolean isValid() {
+ return
+ // First check if all version are not uninitialized.
+ mode != null &&
+ ecLevel != null &&
+ version != -1 &&
+ matrixWidth != -1 &&
+ maskPattern != -1 &&
+ numTotalBytes != -1 &&
+ numDataBytes != -1 &&
+ numECBytes != -1 &&
+ numRSBlocks != -1 &&
+ // Then check them in other ways..
+ isValidMaskPattern(maskPattern) &&
+ numTotalBytes == numDataBytes + numECBytes &&
+ // ByteMatrix stuff.
+ matrix != null &&
+ matrixWidth == matrix.getWidth() &&
+ // See 7.3.1 of JISX0510:2004 (p.5).
+ matrix.getWidth() == matrix.getHeight(); // Must be square.
+ }
+
+ // Return debug String.
+ public String toString() {
+ StringBuffer result = new StringBuffer(200);
+ result.append("<<\n");
+ result.append(" mode: ");
+ result.append(mode);
+ result.append("\n ecLevel: ");
+ result.append(ecLevel);
+ result.append("\n version: ");
+ result.append(version);
+ result.append("\n matrixWidth: ");
+ result.append(matrixWidth);
+ result.append("\n maskPattern: ");
+ result.append(maskPattern);
+ result.append("\n numTotalBytes: ");
+ result.append(numTotalBytes);
+ result.append("\n numDataBytes: ");
+ result.append(numDataBytes);
+ result.append("\n numECBytes: ");
+ result.append(numECBytes);
+ result.append("\n numRSBlocks: ");
+ result.append(numRSBlocks);
+ if (matrix == null) {
+ result.append("\n matrix: null\n");
+ } else {
+ result.append("\n matrix:\n");
+ result.append(matrix.toString());
+ }
+ result.append(">>\n");
+ return result.toString();
+ }
+
+ public void setMode(Mode value) {
+ mode = value;
+ }
+
+ public void setECLevel(ErrorCorrectionLevel value) {
+ ecLevel = value;
+ }
+
+ public void setVersion(int value) {
+ version = value;
+ }
+
+ public void setMatrixWidth(int value) {
+ matrixWidth = value;
+ }
+
+ public void setMaskPattern(int value) {
+ maskPattern = value;
+ }
+
+ public void setNumTotalBytes(int value) {
+ numTotalBytes = value;
+ }
+
+ public void setNumDataBytes(int value) {
+ numDataBytes = value;
+ }
+
+ public void setNumECBytes(int value) {
+ numECBytes = value;
+ }
+
+ public void setNumRSBlocks(int value) {
+ numRSBlocks = value;
+ }
+
+ // This takes ownership of the 2D array.
+ public void setMatrix(ByteMatrix value) {
+ matrix = value;
+ }
+
+ // Check if "mask_pattern" is valid.
+ public static boolean isValidMaskPattern(int maskPattern) {
+ return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
+ }
+
+ // Return true if the all values in the matrix are binary numbers.
+ //
+ // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
+ // production. I'm leaving it because it may be useful for testing. It should be removed entirely
+ // if ByteMatrix is changed never to contain a -1.
+ /*
+ private static boolean EverythingIsBinary(final ByteMatrix matrix) {
+ for (int y = 0; y < matrix.height(); ++y) {
+ for (int x = 0; x < matrix.width(); ++x) {
+ int value = matrix.get(y, x);
+ if (!(value == 0 || value == 1)) {
+ // Found non zero/one value.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ */
+
+}