diff options
author | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-01-12 15:50:09 -0800 |
---|---|---|
committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2014-01-12 15:50:09 -0800 |
commit | 92aa5b36bba57e4927f146d49c9f124a37b7b5f9 (patch) | |
tree | 865d3962e54225fc4f13e0d19852ddc061e7e05f /OpenPGP-Keychain/src/com/google/zxing/qrcode | |
parent | 536c4db48f12287eaf1875e2d208c0ad07e31ee6 (diff) | |
parent | e3bcf64d9e63e293c51db921572a9b87533d26ad (diff) | |
download | open-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.tar.gz open-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.tar.bz2 open-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.zip |
Merge pull request #179 from dschuermann/key-details
Key details
Diffstat (limited to 'OpenPGP-Keychain/src/com/google/zxing/qrcode')
21 files changed, 0 insertions, 4973 deletions
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java deleted file mode 100644 index fff4f5d1e..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java deleted file mode 100644 index 9d131a554..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java +++ /dev/null @@ -1,203 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java deleted file mode 100644 index 12959d9c1..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java +++ /dev/null @@ -1,123 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java deleted file mode 100644 index d29dbd47f..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java +++ /dev/null @@ -1,155 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java deleted file mode 100644 index ff374ac50..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java deleted file mode 100644 index e8d6c2589..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java deleted file mode 100644 index 1b76b0de5..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java deleted file mode 100644 index 3c66217d3..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java deleted file mode 100644 index ba795de42..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java +++ /dev/null @@ -1,586 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java deleted file mode 100644 index 6fc1a2c88..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java deleted file mode 100644 index 3aadf284f..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java +++ /dev/null @@ -1,279 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java deleted file mode 100644 index 724d39d59..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java deleted file mode 100644 index 7a9914d76..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java deleted file mode 100644 index 01b3bde2a..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java +++ /dev/null @@ -1,585 +0,0 @@ -/*
- * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java deleted file mode 100644 index 3c3401085..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java deleted file mode 100644 index 5714d9c3a..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java deleted file mode 100644 index eb248a26c..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java deleted file mode 100644 index 8796511ab..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java +++ /dev/null @@ -1,557 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java deleted file mode 100644 index 61ccf48c1..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java deleted file mode 100644 index 3d434e675..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * 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/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java b/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java deleted file mode 100644 index 05c818513..000000000 --- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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; - } - */ - -} |