diff --git a/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java b/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java index c55481c6a..de54e605c 100755 --- a/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java +++ b/android/src/com/google/zxing/client/android/encode/QRCodeEncoder.java @@ -26,7 +26,7 @@ import com.google.zxing.client.android.R; import com.google.zxing.client.result.AddressBookParsedResult; import com.google.zxing.client.result.ParsedResult; import com.google.zxing.client.result.ResultParser; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import android.app.Activity; import android.content.Intent; @@ -53,6 +53,8 @@ final class QRCodeEncoder { private static final String TAG = "QRCodeEncoder"; + private static final int WHITE = 0xFFFFFFFF; + private final Activity activity; private String contents; private String displayContents; @@ -327,17 +329,18 @@ final class QRCodeEncoder { @Override public void run() { try { - ByteMatrix result = new MultiFormatWriter().encode(contents, format, + BitMatrix result = new MultiFormatWriter().encode(contents, format, pixelResolution, pixelResolution); int width = result.getWidth(); int height = result.getHeight(); - byte[][] array = result.getArray(); int[] pixels = new int[width * height]; + // All are 0, or black, by default for (int y = 0; y < height; y++) { + int offset = y * width; for (int x = 0; x < width; x++) { - int grey = array[y][x] & 0xff; - // pixels[y * width + x] = (0xff << 24) | (grey << 16) | (grey << 8) | grey; - pixels[y * width + x] = 0xff000000 | (0x00010101 * grey); + if (!result.get(x, y)) { + pixels[offset + x] = WHITE; + } } } diff --git a/core/src/com/google/zxing/MultiFormatWriter.java b/core/src/com/google/zxing/MultiFormatWriter.java index 3ec89794e..297b79edc 100644 --- a/core/src/com/google/zxing/MultiFormatWriter.java +++ b/core/src/com/google/zxing/MultiFormatWriter.java @@ -16,7 +16,7 @@ package com.google.zxing; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import com.google.zxing.oned.Code128Writer; import com.google.zxing.oned.Code39Writer; import com.google.zxing.oned.EAN13Writer; @@ -34,13 +34,13 @@ import java.util.Hashtable; */ public final class MultiFormatWriter implements Writer { - public ByteMatrix encode(String contents, BarcodeFormat format, int width, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) throws WriterException { return encode(contents, format, width, height, null); } - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { Writer writer; diff --git a/core/src/com/google/zxing/Writer.java b/core/src/com/google/zxing/Writer.java index 221d5adbf..6474ca7e2 100644 --- a/core/src/com/google/zxing/Writer.java +++ b/core/src/com/google/zxing/Writer.java @@ -16,7 +16,7 @@ package com.google.zxing; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import java.util.Hashtable; @@ -36,7 +36,7 @@ public interface Writer { * @param height The preferred height in pixels * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) */ - ByteMatrix encode(String contents, BarcodeFormat format, int width, int height) + BitMatrix encode(String contents, BarcodeFormat format, int width, int height) throws WriterException; /** @@ -48,7 +48,7 @@ public interface Writer { * @param hints Additional parameters to supply to the encoder * @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white) */ - ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) + BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException; } diff --git a/core/src/com/google/zxing/common/BitArray.java b/core/src/com/google/zxing/common/BitArray.java index bab2a3a66..6c150bf8b 100644 --- a/core/src/com/google/zxing/common/BitArray.java +++ b/core/src/com/google/zxing/common/BitArray.java @@ -28,12 +28,14 @@ public final class BitArray { // resulting binary at runtime on Android. If we find a solution to this, these should be changed // back to private. public int[] bits; - public final int size; + public int size; + + public BitArray() { + this.size = 0; + this.bits = new int[1]; + } public BitArray(int size) { - if (size < 1) { - throw new IllegalArgumentException("size must be at least 1"); - } this.size = size; this.bits = makeArray(size); } @@ -42,6 +44,18 @@ public final class BitArray { return size; } + public int getSizeInBytes() { + return (size + 7) >> 3; + } + + private void ensureCapacity(int size) { + if (size > bits.length << 5) { + int[] newBits = makeArray(size); + System.arraycopy(bits, 0, newBits, 0, bits.length); + this.bits = newBits; + } + } + /** * @param i bit to get * @return true iff bit i is set @@ -130,6 +144,69 @@ public final class BitArray { return true; } + public void appendBit(boolean bit) { + ensureCapacity(size + 1); + if (bit) { + bits[size >> 5] |= (1 << (size & 0x1F)); + } + size++; + } + + /** + * Appends the least-significant bits, from value, in order from most-significant to + * least-significant. For example, appending 6 bits from 0x000001E will append the bits + * 0, 1, 1, 1, 1, 0 in that order. + */ + public void appendBits(int value, int numBits) { + if (numBits < 0 || numBits > 32) { + throw new IllegalArgumentException("Num bits must be between 0 and 32"); + } + ensureCapacity(size + numBits); + for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) { + appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1); + } + } + + public void appendBitArray(BitArray other) { + int otherSize = other.getSize(); + ensureCapacity(size + otherSize); + for (int i = 0; i < otherSize; i++) { + appendBit(other.get(i)); + } + } + + public void xor(BitArray other) { + if (bits.length != other.bits.length) { + throw new IllegalArgumentException("Sizes don't match"); + } + for (int i = 0; i < bits.length; i++) { + // The last byte could be incomplete (i.e. not have 8 bits in + // it) but there is no problem since 0 XOR 0 == 0. + bits[i] ^= other.bits[i]; + } + } + + /** + * + * @param bitOffset first bit to start writing + * @param array array to write into. Bytes are written most-significant byte first. This is the opposite + * of the internal representation, which is exposed by {@link #getBitArray()} + * @param offset position in array to start writing + * @param numBytes how many bytes to write + */ + public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) { + for (int i = 0; i < numBytes; i++) { + int theByte = 0; + for (int j = 0; j < 8; j++) { + if (get(bitOffset)) { + theByte |= 1 << (7 - j); + } + bitOffset++; + } + array[offset + i] = (byte) theByte; + } + } + /** * @return underlying array of ints. The first element holds the first 32 bits, and the least * significant bit is bit 0. @@ -153,11 +230,7 @@ public final class BitArray { } private static int[] makeArray(int size) { - int arraySize = size >> 5; - if ((size & 0x1F) != 0) { - arraySize++; - } - return new int[arraySize]; + return new int[(size + 31) >> 5]; } public String toString() { diff --git a/core/src/com/google/zxing/common/BitMatrix.java b/core/src/com/google/zxing/common/BitMatrix.java index 1088db2f7..f1023b8b5 100755 --- a/core/src/com/google/zxing/common/BitMatrix.java +++ b/core/src/com/google/zxing/common/BitMatrix.java @@ -50,11 +50,7 @@ public final class BitMatrix { } this.width = width; this.height = height; - int rowSize = width >> 5; - if ((width & 0x1f) != 0) { - rowSize++; - } - this.rowSize = rowSize; + this.rowSize = (width + 31) >> 5; bits = new int[rowSize * height]; } @@ -163,17 +159,32 @@ public final class BitMatrix { return height; } - /** - * This method is for compatibility with older code. It's only logical to call if the matrix - * is square, so I'm throwing if that's not the case. - * - * @return row/column dimension of this matrix - */ - public int getDimension() { - if (width != height) { - throw new RuntimeException("Can't call getDimension() on a non-square matrix"); + public boolean equals(Object o) { + if (!(o instanceof BitMatrix)) { + return false; } - return width; + BitMatrix other = (BitMatrix) o; + if (width != other.width || height != other.height || + rowSize != other.rowSize || bits.length != other.bits.length) { + return false; + } + for (int i = 0; i < bits.length; i++) { + if (bits[i] != other.bits[i]) { + return false; + } + } + return true; + } + + public int hashCode() { + int hash = width; + hash = 31 * hash + width; + hash = 31 * hash + height; + hash = 31 * hash + rowSize; + for (int i = 0; i < bits.length; i++) { + hash = 31 * hash + bits[i]; + } + return hash; } public String toString() { diff --git a/core/src/com/google/zxing/common/ByteArray.java b/core/src/com/google/zxing/common/ByteArray.java deleted file mode 100644 index 58116f03b..000000000 --- a/core/src/com/google/zxing/common/ByteArray.java +++ /dev/null @@ -1,95 +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.common; - -/** - * This class implements an array of unsigned bytes. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class ByteArray { - - private static final int INITIAL_SIZE = 32; - - private byte[] bytes; - private int size; - - public ByteArray() { - bytes = null; - size = 0; - } - - public ByteArray(int size) { - bytes = new byte[size]; - this.size = size; - } - - public ByteArray(byte[] byteArray) { - bytes = byteArray; - size = bytes.length; - } - - /** - * Access an unsigned byte at location index. - * @param index The index in the array to access. - * @return The unsigned value of the byte as an int. - */ - public int at(int index) { - return bytes[index] & 0xff; - } - - public void set(int index, int value) { - bytes[index] = (byte) value; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return size == 0; - } - - public void appendByte(int value) { - if (size == 0 || size >= bytes.length) { - int newSize = Math.max(INITIAL_SIZE, size << 1); - reserve(newSize); - } - bytes[size] = (byte) value; - size++; - } - - public void reserve(int capacity) { - if (bytes == null || bytes.length < capacity) { - byte[] newArray = new byte[capacity]; - if (bytes != null) { - System.arraycopy(bytes, 0, newArray, 0, bytes.length); - } - bytes = newArray; - } - } - - // Copy count bytes from array source starting at offset. - public void set(byte[] source, int offset, int count) { - bytes = new byte[count]; - size = count; - for (int x = 0; x < count; x++) { - bytes[x] = source[offset + x]; - } - } - -} diff --git a/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java b/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java index 849f5be08..96032b435 100644 --- a/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java +++ b/core/src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java @@ -33,7 +33,7 @@ final class BitMatrixParser { * @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2 */ BitMatrixParser(BitMatrix bitMatrix) throws FormatException { - int dimension = bitMatrix.getDimension(); + int dimension = bitMatrix.getHeight(); if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) { throw FormatException.getFormatInstance(); } @@ -41,7 +41,7 @@ final class BitMatrixParser { version = readVersion(bitMatrix); this.mappingBitMatrix = extractDataRegion(bitMatrix); // TODO(bbrown): Make this work for rectangular symbols - this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getDimension()); + this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight()); } /** @@ -62,7 +62,7 @@ final class BitMatrixParser { } // TODO(bbrown): make this work for rectangular dimensions as well. - int numRows = bitMatrix.getDimension(); + int numRows = bitMatrix.getHeight(); int numColumns = numRows; return Version.getVersionForDimensions(numRows, numColumns); @@ -84,7 +84,7 @@ final class BitMatrixParser { int row = 4; int column = 0; // TODO(bbrown): Data Matrix can be rectangular, assuming square for now - int numRows = mappingBitMatrix.getDimension(); + int numRows = mappingBitMatrix.getHeight(); int numColumns = numRows; boolean corner1Read = false; @@ -408,7 +408,7 @@ final class BitMatrixParser { int symbolSizeColumns = version.getSymbolSizeColumns(); // TODO(bbrown): Make this work with rectangular codes - if (bitMatrix.getDimension() != symbolSizeRows) { + if (bitMatrix.getHeight() != symbolSizeRows) { throw new IllegalArgumentException("Dimension of bitMarix must match the version size"); } diff --git a/core/src/com/google/zxing/oned/Code128Writer.java b/core/src/com/google/zxing/oned/Code128Writer.java index 9f4734b91..0c2262618 100644 --- a/core/src/com/google/zxing/oned/Code128Writer.java +++ b/core/src/com/google/zxing/oned/Code128Writer.java @@ -19,7 +19,7 @@ package com.google.zxing.oned; import java.util.Hashtable; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; /** * This object renders a CODE128 code as a {@link BitMatrix}. @@ -28,7 +28,7 @@ import com.google.zxing.common.ByteMatrix; */ public final class Code128Writer extends UPCEANWriter { - public ByteMatrix encode(String contents, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, diff --git a/core/src/com/google/zxing/oned/Code39Writer.java b/core/src/com/google/zxing/oned/Code39Writer.java index 42ab1fd9c..fab63b2fd 100644 --- a/core/src/com/google/zxing/oned/Code39Writer.java +++ b/core/src/com/google/zxing/oned/Code39Writer.java @@ -19,7 +19,7 @@ package com.google.zxing.oned; import java.util.Hashtable; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; /** * This object renders a CODE39 code as a {@link BitMatrix}. @@ -28,7 +28,7 @@ import com.google.zxing.common.ByteMatrix; */ public final class Code39Writer extends UPCEANWriter { - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { if (format != BarcodeFormat.CODE_39) { throw new IllegalArgumentException("Can only encode CODE_39, but got " + format); diff --git a/core/src/com/google/zxing/oned/EAN13Writer.java b/core/src/com/google/zxing/oned/EAN13Writer.java index ff9371d1e..ca6d67a55 100644 --- a/core/src/com/google/zxing/oned/EAN13Writer.java +++ b/core/src/com/google/zxing/oned/EAN13Writer.java @@ -18,14 +18,13 @@ package com.google.zxing.oned; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import java.util.Hashtable; /** - * This object renders an EAN13 code as a ByteMatrix 2D array of greyscale - * values. + * This object renders an EAN13 code as a {@link BitMatrix}. * * @author aripollak@gmail.com (Ari Pollak) */ @@ -37,7 +36,7 @@ public final class EAN13Writer extends UPCEANWriter { (7 * 6) + // right bars 3; // end guard - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { if (format != BarcodeFormat.EAN_13) { throw new IllegalArgumentException("Can only encode EAN_13, but got " + format); diff --git a/core/src/com/google/zxing/oned/EAN8Writer.java b/core/src/com/google/zxing/oned/EAN8Writer.java index b665f5f56..42737074c 100644 --- a/core/src/com/google/zxing/oned/EAN8Writer.java +++ b/core/src/com/google/zxing/oned/EAN8Writer.java @@ -18,13 +18,12 @@ package com.google.zxing.oned; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import java.util.Hashtable; /** - * This object renders an EAN8 code as a ByteMatrix 2D array of greyscale - * values. + * This object renders an EAN8 code as a {@link BitMatrix}. * * @author aripollak@gmail.com (Ari Pollak) */ @@ -36,7 +35,7 @@ public final class EAN8Writer extends UPCEANWriter { (7 * 4) + // right bars 3; // end guard - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { if (format != BarcodeFormat.EAN_8) { throw new IllegalArgumentException("Can only encode EAN_8, but got " diff --git a/core/src/com/google/zxing/oned/ITFWriter.java b/core/src/com/google/zxing/oned/ITFWriter.java index 1847f7b2b..b85a65213 100644 --- a/core/src/com/google/zxing/oned/ITFWriter.java +++ b/core/src/com/google/zxing/oned/ITFWriter.java @@ -19,7 +19,7 @@ package com.google.zxing.oned; import java.util.Hashtable; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; /** * This object renders a ITF code as a {@link BitMatrix}. @@ -28,7 +28,7 @@ import com.google.zxing.common.ByteMatrix; */ public final class ITFWriter extends UPCEANWriter { - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { if (format != BarcodeFormat.ITF) { throw new IllegalArgumentException("Can only encode ITF, but got " + format); diff --git a/core/src/com/google/zxing/oned/UPCEANWriter.java b/core/src/com/google/zxing/oned/UPCEANWriter.java index b65c2070f..68fa4a0cd 100644 --- a/core/src/com/google/zxing/oned/UPCEANWriter.java +++ b/core/src/com/google/zxing/oned/UPCEANWriter.java @@ -19,7 +19,7 @@ package com.google.zxing.oned; import com.google.zxing.BarcodeFormat; import com.google.zxing.Writer; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import java.util.Hashtable; @@ -31,12 +31,12 @@ import java.util.Hashtable; */ public abstract class UPCEANWriter implements Writer { - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height) + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) throws WriterException { return encode(contents, format, width, height, null); } - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + 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"); @@ -52,7 +52,7 @@ public abstract class UPCEANWriter implements Writer { } /** @return a byte array of horizontal pixels (0 = white, 1 = black) */ - private static ByteMatrix renderResult(byte[] code, int width, int height) { + private static BitMatrix renderResult(byte[] code, int width, int height) { int inputWidth = code.length; // Add quiet zone on both sides int fullWidth = inputWidth + (UPCEANReader.START_END_PATTERN.length << 1); @@ -62,37 +62,12 @@ public abstract class UPCEANWriter implements Writer { int multiple = outputWidth / fullWidth; int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; - ByteMatrix output = new ByteMatrix(outputWidth, outputHeight); - byte[][] outputArray = output.getArray(); - - byte[] row = new byte[outputWidth]; - - // a. Write the white pixels at the left of each row - for (int x = 0; x < leftPadding; x++) { - row[x] = (byte) 255; - } - - // b. Write the contents of this row of the barcode - int offset = leftPadding; - for (int x = 0; x < inputWidth; x++) { - byte value = (code[x] == 1) ? 0 : (byte) 255; - for (int z = 0; z < multiple; z++) { - row[offset + z] = value; + BitMatrix output = new BitMatrix(outputWidth, outputHeight); + for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) { + if (code[inputX] == 1) { + output.setRegion(outputX, 0, multiple, outputHeight); } - offset += multiple; } - - // c. Write the white pixels at the right of each row - offset = leftPadding + (inputWidth * multiple); - for (int x = offset; x < outputWidth; x++) { - row[x] = (byte) 255; - } - - // d. Write the completed row multiple times - for (int z = 0; z < outputHeight; z++) { - System.arraycopy(row, 0, outputArray[z], 0, outputWidth); - } - return output; } diff --git a/core/src/com/google/zxing/pdf417/decoder/BitMatrixParser.java b/core/src/com/google/zxing/pdf417/decoder/BitMatrixParser.java index 7821483ad..29c0ba301 100644 --- a/core/src/com/google/zxing/pdf417/decoder/BitMatrixParser.java +++ b/core/src/com/google/zxing/pdf417/decoder/BitMatrixParser.java @@ -60,7 +60,7 @@ final class BitMatrixParser { * @return an array of codewords. */ int[] readCodewords() throws FormatException { - int width = bitMatrix.getDimension(); + int width = bitMatrix.getWidth(); // TODO should be a rectangular matrix int height = width; @@ -186,7 +186,7 @@ final class BitMatrixParser { */ int processRow(int[] rowCounters, int rowNumber, int rowHeight, int[] codewords, int next) throws FormatException { - int width = bitMatrix.getDimension(); + int width = bitMatrix.getWidth(); int columnNumber = 0; long symbol = 0; for (int i = 0; i < width; i += MODULES_IN_SYMBOL) { diff --git a/core/src/com/google/zxing/qrcode/QRCodeWriter.java b/core/src/com/google/zxing/qrcode/QRCodeWriter.java index 7d516c614..c0a5e0af4 100644 --- a/core/src/com/google/zxing/qrcode/QRCodeWriter.java +++ b/core/src/com/google/zxing/qrcode/QRCodeWriter.java @@ -20,7 +20,8 @@ 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.ByteMatrix; +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; @@ -28,7 +29,7 @@ import com.google.zxing.qrcode.encoder.QRCode; import java.util.Hashtable; /** - * This object renders a QR Code as a ByteMatrix 2D array of greyscale values. + * This object renders a QR Code as a BitMatrix 2D array of greyscale values. * * @author dswitkin@google.com (Daniel Switkin) */ @@ -36,13 +37,13 @@ public final class QRCodeWriter implements Writer { private static final int QUIET_ZONE_SIZE = 4; - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height) + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) throws WriterException { return encode(contents, format, width, height, null); } - public ByteMatrix encode(String contents, BarcodeFormat format, int width, int height, + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Hashtable hints) throws WriterException { if (contents == null || contents.length() == 0) { @@ -73,7 +74,7 @@ public final class QRCodeWriter implements Writer { // 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 ByteMatrix renderResult(QRCode code, int width, int height) { + private static BitMatrix renderResult(QRCode code, int width, int height) { ByteMatrix input = code.getMatrix(); int inputWidth = input.getWidth(); int inputHeight = input.getHeight(); @@ -90,62 +91,18 @@ public final class QRCodeWriter implements Writer { int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; int topPadding = (outputHeight - (inputHeight * multiple)) / 2; - ByteMatrix output = new ByteMatrix(outputWidth, outputHeight); - byte[][] outputArray = output.getArray(); + BitMatrix output = new BitMatrix(outputWidth, outputHeight); - // We could be tricky and use the first row in each set of multiple as the temporary storage, - // instead of allocating this separate array. - byte[] row = new byte[outputWidth]; - - // 1. Write the white lines at the top - for (int y = 0; y < topPadding; y++) { - setRowColor(outputArray[y], (byte) 255); - } - - // 2. Expand the QR image to the multiple - byte[][] inputArray = input.getArray(); - for (int y = 0; y < inputHeight; y++) { - // a. Write the white pixels at the left of each row - for (int x = 0; x < leftPadding; x++) { - row[x] = (byte) 255; - } - - // b. Write the contents of this row of the barcode - int offset = leftPadding; - for (int x = 0; x < inputWidth; x++) { - byte value = (inputArray[y][x] == 1) ? 0 : (byte) 255; - for (int z = 0; z < multiple; z++) { - row[offset + z] = value; + 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); } - offset += multiple; } - - // c. Write the white pixels at the right of each row - offset = leftPadding + (inputWidth * multiple); - for (int x = offset; x < outputWidth; x++) { - row[x] = (byte) 255; - } - - // d. Write the completed row multiple times - offset = topPadding + (y * multiple); - for (int z = 0; z < multiple; z++) { - System.arraycopy(row, 0, outputArray[offset + z], 0, outputWidth); - } - } - - // 3. Write the white lines at the bottom - int offset = topPadding + (inputHeight * multiple); - for (int y = offset; y < outputHeight; y++) { - setRowColor(outputArray[y], (byte) 255); } return output; } - private static void setRowColor(byte[] row, byte value) { - for (int x = 0; x < row.length; x++) { - row[x] = value; - } - } - } diff --git a/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java index 2a820ec84..d8eaad91b 100644 --- a/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java +++ b/core/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java @@ -33,7 +33,7 @@ final class BitMatrixParser { * @throws FormatException if dimension is not >= 21 and 1 mod 4 */ BitMatrixParser(BitMatrix bitMatrix) throws FormatException { - int dimension = bitMatrix.getDimension(); + int dimension = bitMatrix.getHeight(); if (dimension < 21 || (dimension & 0x03) != 1) { throw FormatException.getFormatInstance(); } @@ -68,7 +68,7 @@ final class BitMatrixParser { } // Read the top-right/bottom-left pattern too - int dimension = bitMatrix.getDimension(); + int dimension = bitMatrix.getHeight(); int formatInfoBits2 = 0; int iMin = dimension - 8; for (int i = dimension - 1; i >= iMin; i--) { @@ -98,7 +98,7 @@ final class BitMatrixParser { return parsedVersion; } - int dimension = bitMatrix.getDimension(); + int dimension = bitMatrix.getHeight(); int provisionalVersion = (dimension - 17) >> 2; if (provisionalVersion <= 6) { @@ -154,7 +154,7 @@ final class BitMatrixParser { // Get the data mask for the format used in this QR Code. This will exclude // some bits from reading as we wind through the bit matrix. DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask()); - int dimension = bitMatrix.getDimension(); + int dimension = bitMatrix.getHeight(); dataMask.unmaskBitMatrix(bitMatrix, dimension); BitMatrix functionPattern = version.buildFunctionPattern(); diff --git a/core/src/com/google/zxing/qrcode/encoder/BitVector.java b/core/src/com/google/zxing/qrcode/encoder/BitVector.java deleted file mode 100644 index a78078b9a..000000000 --- a/core/src/com/google/zxing/qrcode/encoder/BitVector.java +++ /dev/null @@ -1,155 +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; - -/** - * JAVAPORT: This should be combined with BitArray in the future, although that class is not yet - * dynamically resizeable. This implementation is reasonable but there is a lot of function calling - * in loops I'd like to get rid of. - * - * @author satorux@google.com (Satoru Takabayashi) - creator - * @author dswitkin@google.com (Daniel Switkin) - ported from C++ - */ -public final class BitVector { - - private int sizeInBits; - private byte[] array; - - // For efficiency, start out with some room to work. - private static final int DEFAULT_SIZE_IN_BYTES = 32; - - public BitVector() { - sizeInBits = 0; - array = new byte[DEFAULT_SIZE_IN_BYTES]; - } - - // Return the bit value at "index". - public int at(int index) { - if (index < 0 || index >= sizeInBits) { - throw new IllegalArgumentException("Bad index: " + index); - } - int value = array[index >> 3] & 0xff; - return (value >> (7 - (index & 0x7))) & 1; - } - - // Return the number of bits in the bit vector. - public int size() { - return sizeInBits; - } - - // Return the number of bytes in the bit vector. - public int sizeInBytes() { - return (sizeInBits + 7) >> 3; - } - - // Append one bit to the bit vector. - public void appendBit(int bit) { - if (!(bit == 0 || bit == 1)) { - throw new IllegalArgumentException("Bad bit"); - } - int numBitsInLastByte = sizeInBits & 0x7; - // We'll expand array if we don't have bits in the last byte. - if (numBitsInLastByte == 0) { - appendByte(0); - sizeInBits -= 8; - } - // Modify the last byte. - array[sizeInBits >> 3] |= (bit << (7 - numBitsInLastByte)); - ++sizeInBits; - } - - // Append "numBits" bits in "value" to the bit vector. - // REQUIRES: 0<= numBits <= 32. - // - // Examples: - // - appendBits(0x00, 1) adds 0. - // - appendBits(0x00, 4) adds 0000. - // - appendBits(0xff, 8) adds 11111111. - public void appendBits(int value, int numBits) { - if (numBits < 0 || numBits > 32) { - throw new IllegalArgumentException("Num bits must be between 0 and 32"); - } - int numBitsLeft = numBits; - while (numBitsLeft > 0) { - // Optimization for byte-oriented appending. - if ((sizeInBits & 0x7) == 0 && numBitsLeft >= 8) { - int newByte = (value >> (numBitsLeft - 8)) & 0xff; - appendByte(newByte); - numBitsLeft -= 8; - } else { - int bit = (value >> (numBitsLeft - 1)) & 1; - appendBit(bit); - --numBitsLeft; - } - } - } - - // Append "bits". - public void appendBitVector(BitVector bits) { - int size = bits.size(); - for (int i = 0; i < size; ++i) { - appendBit(bits.at(i)); - } - } - - // Modify the bit vector by XOR'ing with "other" - public void xor(BitVector other) { - if (sizeInBits != other.size()) { - throw new IllegalArgumentException("BitVector sizes don't match"); - } - int sizeInBytes = (sizeInBits + 7) >> 3; - for (int i = 0; i < sizeInBytes; ++i) { - // The last byte could be incomplete (i.e. not have 8 bits in - // it) but there is no problem since 0 XOR 0 == 0. - array[i] ^= other.array[i]; - } - } - - // Return String like "01110111" for debugging. - public String toString() { - StringBuffer result = new StringBuffer(sizeInBits); - for (int i = 0; i < sizeInBits; ++i) { - if (at(i) == 0) { - result.append('0'); - } else if (at(i) == 1) { - result.append('1'); - } else { - throw new IllegalArgumentException("Byte isn't 0 or 1"); - } - } - return result.toString(); - } - - // Callers should not assume that array.length is the exact number of bytes needed to hold - // sizeInBits - it will typically be larger for efficiency. - public byte[] getArray() { - return array; - } - - // Add a new byte to the end, possibly reallocating and doubling the size of the array if we've - // run out of room. - private void appendByte(int value) { - if ((sizeInBits >> 3) == array.length) { - byte[] newArray = new byte[(array.length << 1)]; - System.arraycopy(array, 0, newArray, 0, array.length); - array = newArray; - } - array[sizeInBits >> 3] = (byte) value; - sizeInBits += 8; - } - -} diff --git a/core/src/com/google/zxing/qrcode/encoder/BlockPair.java b/core/src/com/google/zxing/qrcode/encoder/BlockPair.java index fece49978..5714d9c3a 100644 --- a/core/src/com/google/zxing/qrcode/encoder/BlockPair.java +++ b/core/src/com/google/zxing/qrcode/encoder/BlockPair.java @@ -16,23 +16,21 @@ package com.google.zxing.qrcode.encoder; -import com.google.zxing.common.ByteArray; - final class BlockPair { - private final ByteArray dataBytes; - private final ByteArray errorCorrectionBytes; + private final byte[] dataBytes; + private final byte[] errorCorrectionBytes; - BlockPair(ByteArray data, ByteArray errorCorrection) { + BlockPair(byte[] data, byte[] errorCorrection) { dataBytes = data; errorCorrectionBytes = errorCorrection; } - public ByteArray getDataBytes() { + public byte[] getDataBytes() { return dataBytes; } - public ByteArray getErrorCorrectionBytes() { + public byte[] getErrorCorrectionBytes() { return errorCorrectionBytes; } diff --git a/core/src/com/google/zxing/common/ByteMatrix.java b/core/src/com/google/zxing/qrcode/encoder/ByteMatrix.java similarity index 94% rename from core/src/com/google/zxing/common/ByteMatrix.java rename to core/src/com/google/zxing/qrcode/encoder/ByteMatrix.java index 9ac01bc6f..eb248a26c 100644 --- a/core/src/com/google/zxing/common/ByteMatrix.java +++ b/core/src/com/google/zxing/qrcode/encoder/ByteMatrix.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.zxing.common; +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 @@ -61,6 +61,10 @@ public final class ByteMatrix { 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) { diff --git a/core/src/com/google/zxing/qrcode/encoder/Encoder.java b/core/src/com/google/zxing/qrcode/encoder/Encoder.java index fd02c3ba4..2217b36a8 100644 --- a/core/src/com/google/zxing/qrcode/encoder/Encoder.java +++ b/core/src/com/google/zxing/qrcode/encoder/Encoder.java @@ -18,8 +18,7 @@ package com.google.zxing.qrcode.encoder; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteArray; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitArray; import com.google.zxing.common.CharacterSetECI; import com.google.zxing.common.reedsolomon.GF256; import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; @@ -91,14 +90,14 @@ public final class Encoder { Mode mode = chooseMode(content, encoding); // Step 2: Append "bytes" into "dataBits" in appropriate encoding. - BitVector dataBits = new BitVector(); + BitArray dataBits = new BitArray(); appendBytes(content, mode, dataBits, encoding); // Step 3: Initialize QR code that can contain "dataBits". - int numInputBytes = dataBits.sizeInBytes(); + int numInputBytes = dataBits.getSizeInBytes(); initQRCode(numInputBytes, ecLevel, mode, qrCode); // Step 4: Build another bit vector that contains header and data. - BitVector headerAndDataBits = new BitVector(); + BitArray headerAndDataBits = new BitArray(); // Step 4.5: Append ECI message if applicable if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) { @@ -110,15 +109,15 @@ public final class Encoder { appendModeInfo(mode, headerAndDataBits); - int numLetters = mode.equals(Mode.BYTE) ? dataBits.sizeInBytes() : content.length(); + int numLetters = mode.equals(Mode.BYTE) ? dataBits.getSizeInBytes() : content.length(); appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits); - headerAndDataBits.appendBitVector(dataBits); + headerAndDataBits.appendBitArray(dataBits); // Step 5: Terminate the bits properly. terminateBits(qrCode.getNumDataBytes(), headerAndDataBits); // Step 6: Interleave data bits with error correction code. - BitVector finalBits = new BitVector(); + BitArray finalBits = new BitArray(); interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(), qrCode.getNumRSBlocks(), finalBits); @@ -201,7 +200,7 @@ public final class Encoder { return true; } - private static int chooseMaskPattern(BitVector bits, ErrorCorrectionLevel ecLevel, int version, + private static int chooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, int version, ByteMatrix matrix) throws WriterException { int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. @@ -261,41 +260,29 @@ public final class Encoder { /** * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). */ - static void terminateBits(int numDataBytes, BitVector bits) throws WriterException { + static void terminateBits(int numDataBytes, BitArray bits) throws WriterException { int capacity = numDataBytes << 3; - if (bits.size() > capacity) { - throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + + 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. - // TODO: srowen says we can remove this for loop, since the 4 terminator bits are optional if - // the last byte has less than 4 bits left. So it amounts to padding the last byte with zeroes - // either way. - for (int i = 0; i < 4 && bits.size() < capacity; ++i) { - bits.appendBit(0); - } - int numBitsInLastByte = bits.size() % 8; // If the last byte isn't 8-bit aligned, we'll add padding bits. + int numBitsInLastByte = bits.getSize() & 0x07; if (numBitsInLastByte > 0) { - int numPaddingBits = 8 - numBitsInLastByte; - for (int i = 0; i < numPaddingBits; ++i) { - bits.appendBit(0); + for (int i = numBitsInLastByte; i < 8; i++) { + bits.appendBit(false); } } - // Should be 8-bit aligned here. - if (bits.size() % 8 != 0) { - throw new WriterException("Number of bits is not a multiple of 8"); - } // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). - int numPaddingBytes = numDataBytes - bits.sizeInBytes(); + int numPaddingBytes = numDataBytes - bits.getSizeInBytes(); for (int i = 0; i < numPaddingBytes; ++i) { - if (i % 2 == 0) { - bits.appendBits(0xec, 8); - } else { - bits.appendBits(0x11, 8); - } + bits.appendBits(((i & 0x01) == 0) ? 0xEC : 0x11, 8); } - if (bits.size() != capacity) { + if (bits.getSize() != capacity) { throw new WriterException("Bits size does not equal capacity"); } } @@ -358,11 +345,11 @@ public final class Encoder { * 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(BitVector bits, int numTotalBytes, - int numDataBytes, int numRSBlocks, BitVector result) throws WriterException { + static void interleaveWithECBytes(BitArray bits, int numTotalBytes, + int numDataBytes, int numRSBlocks, BitArray result) throws WriterException { // "bits" must have "getNumDataBytes" bytes of data. - if (bits.sizeInBytes() != numDataBytes) { + if (bits.getSizeInBytes() != numDataBytes) { throw new WriterException("Number of bits and data bytes does not match"); } @@ -382,13 +369,14 @@ public final class Encoder { numTotalBytes, numDataBytes, numRSBlocks, i, numDataBytesInBlock, numEcBytesInBlock); - ByteArray dataBytes = new ByteArray(); - dataBytes.set(bits.getArray(), dataBytesOffset, numDataBytesInBlock[0]); - ByteArray ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); + 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, dataBytes.size()); - maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.size()); + maxNumDataBytes = Math.max(maxNumDataBytes, size); + maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length); dataBytesOffset += numDataBytesInBlock[0]; } if (numDataBytes != dataBytesOffset) { @@ -398,38 +386,38 @@ public final class Encoder { // First, place data blocks. for (int i = 0; i < maxNumDataBytes; ++i) { for (int j = 0; j < blocks.size(); ++j) { - ByteArray dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes(); - if (i < dataBytes.size()) { - result.appendBits(dataBytes.at(i), 8); + 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) { - ByteArray ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes(); - if (i < ecBytes.size()) { - result.appendBits(ecBytes.at(i), 8); + byte[] ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes(); + if (i < ecBytes.length) { + result.appendBits(ecBytes[i], 8); } } } - if (numTotalBytes != result.sizeInBytes()) { // Should be same. + if (numTotalBytes != result.getSizeInBytes()) { // Should be same. throw new WriterException("Interleaving error: " + numTotalBytes + " and " + - result.sizeInBytes() + " differ."); + result.getSizeInBytes() + " differ."); } } - static ByteArray generateECBytes(ByteArray dataBytes, int numEcBytesInBlock) { - int numDataBytes = dataBytes.size(); + 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.at(i); + toEncode[i] = dataBytes[i] & 0xFF; } new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, numEcBytesInBlock); - ByteArray ecBytes = new ByteArray(numEcBytesInBlock); + byte[] ecBytes = new byte[numEcBytesInBlock]; for (int i = 0; i < numEcBytesInBlock; i++) { - ecBytes.set(i, toEncode[numDataBytes + i]); + ecBytes[i] = (byte) toEncode[numDataBytes + i]; } return ecBytes; } @@ -437,7 +425,7 @@ public final class Encoder { /** * Append mode info. On success, store the result in "bits". */ - static void appendModeInfo(Mode mode, BitVector bits) { + static void appendModeInfo(Mode mode, BitArray bits) { bits.appendBits(mode.getBits(), 4); } @@ -445,7 +433,7 @@ public final class Encoder { /** * Append length info. On success, store the result in "bits". */ - static void appendLengthInfo(int numLetters, int version, Mode mode, BitVector 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)) { @@ -457,7 +445,7 @@ public final class Encoder { /** * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". */ - static void appendBytes(String content, Mode mode, BitVector bits, String encoding) + static void appendBytes(String content, Mode mode, BitArray bits, String encoding) throws WriterException { if (mode.equals(Mode.NUMERIC)) { appendNumericBytes(content, bits); @@ -472,7 +460,7 @@ public final class Encoder { } } - static void appendNumericBytes(String content, BitVector bits) { + static void appendNumericBytes(String content, BitArray bits) { int length = content.length(); int i = 0; while (i < length) { @@ -496,7 +484,7 @@ public final class Encoder { } } - static void appendAlphanumericBytes(String content, BitVector bits) throws WriterException { + static void appendAlphanumericBytes(String content, BitArray bits) throws WriterException { int length = content.length(); int i = 0; while (i < length) { @@ -520,7 +508,7 @@ public final class Encoder { } } - static void append8BitBytes(String content, BitVector bits, String encoding) + static void append8BitBytes(String content, BitArray bits, String encoding) throws WriterException { byte[] bytes; try { @@ -533,7 +521,7 @@ public final class Encoder { } } - static void appendKanjiBytes(String content, BitVector bits) throws WriterException { + static void appendKanjiBytes(String content, BitArray bits) throws WriterException { byte[] bytes; try { bytes = content.getBytes("Shift_JIS"); @@ -559,7 +547,7 @@ public final class Encoder { } } - private static void appendECI(CharacterSetECI eci, BitVector bits) { + private static void appendECI(CharacterSetECI 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/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java index e4806e528..c7f3c48ad 100644 --- a/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java +++ b/core/src/com/google/zxing/qrcode/encoder/MaskUtil.java @@ -16,8 +16,6 @@ package com.google.zxing.qrcode.encoder; -import com.google.zxing.common.ByteMatrix; - /** * @author satorux@google.com (Satoru Takabayashi) - creator * @author dswitkin@google.com (Daniel Switkin) - ported from C++ diff --git a/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java index c4c007f2f..542159ea1 100644 --- a/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java +++ b/core/src/com/google/zxing/qrcode/encoder/MatrixUtil.java @@ -17,7 +17,7 @@ package com.google.zxing.qrcode.encoder; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitArray; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; /** @@ -136,7 +136,7 @@ public final class MatrixUtil { // 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(BitVector dataBits, ErrorCorrectionLevel ecLevel, int version, + public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, int version, int maskPattern, ByteMatrix matrix) throws WriterException { clearMatrix(matrix); embedBasicPatterns(version, matrix); @@ -169,13 +169,13 @@ public final class MatrixUtil { // Embed type information. On success, modify the matrix. public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix) throws WriterException { - BitVector typeInfoBits = new BitVector(); + BitArray typeInfoBits = new BitArray(); makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits); - for (int i = 0; i < typeInfoBits.size(); ++i) { + 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". - int bit = typeInfoBits.at(typeInfoBits.size() - 1 - i); + 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]; @@ -202,14 +202,14 @@ public final class MatrixUtil { if (version < 7) { // Version info is necessary if version >= 7. return; // Don't need version info. } - BitVector versionInfoBits = new BitVector(); + 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. - int bit = versionInfoBits.at(bitIndex); + boolean bit = versionInfoBits.get(bitIndex); bitIndex--; // Left bottom corner. matrix.set(i, matrix.getHeight() - 11 + j, bit); @@ -222,7 +222,7 @@ public final class MatrixUtil { // 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(BitVector dataBits, int maskPattern, ByteMatrix matrix) + public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix) throws WriterException { int bitIndex = 0; int direction = -1; @@ -241,20 +241,20 @@ public final class MatrixUtil { if (!isEmpty(matrix.get(xx, y))) { continue; } - int bit; - if (bitIndex < dataBits.size()) { - bit = dataBits.at(bitIndex); + 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 = 0; + bit = false; } // Skip masking if mask_pattern is -1. if (maskPattern != -1) { if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) { - bit ^= 0x1; + bit = !bit; } } matrix.set(xx, y, bit); @@ -266,8 +266,8 @@ public final class MatrixUtil { x -= 2; // Move to the left. } // All bits should be consumed. - if (bitIndex != dataBits.size()) { - throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.size()); + if (bitIndex != dataBits.getSize()) { + throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize()); } } @@ -326,7 +326,7 @@ public final class MatrixUtil { // 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, BitVector bits) + public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits) throws WriterException { if (!QRCode.isValidMaskPattern(maskPattern)) { throw new WriterException("Invalid mask pattern"); @@ -337,24 +337,24 @@ public final class MatrixUtil { int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY); bits.appendBits(bchCode, 10); - BitVector maskBits = new BitVector(); + BitArray maskBits = new BitArray(); maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15); bits.xor(maskBits); - if (bits.size() != 15) { // Just in case. - throw new WriterException("should not happen but we got: " + bits.size()); + 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, BitVector bits) throws WriterException { + 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.size() != 18) { // Just in case. - throw new WriterException("should not happen but we got: " + bits.size()); + if (bits.getSize() != 18) { // Just in case. + throw new WriterException("should not happen but we got: " + bits.getSize()); } } diff --git a/core/src/com/google/zxing/qrcode/encoder/QRCode.java b/core/src/com/google/zxing/qrcode/encoder/QRCode.java index 75ec9e72d..05c818513 100644 --- a/core/src/com/google/zxing/qrcode/encoder/QRCode.java +++ b/core/src/com/google/zxing/qrcode/encoder/QRCode.java @@ -16,7 +16,6 @@ package com.google.zxing.qrcode.encoder; -import com.google.zxing.common.ByteMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.Mode; diff --git a/core/test/src/com/google/zxing/common/BitMatrixTestCase.java b/core/test/src/com/google/zxing/common/BitMatrixTestCase.java index 91c876928..5f140666b 100644 --- a/core/test/src/com/google/zxing/common/BitMatrixTestCase.java +++ b/core/test/src/com/google/zxing/common/BitMatrixTestCase.java @@ -26,7 +26,7 @@ public final class BitMatrixTestCase extends TestCase { public void testGetSet() { BitMatrix matrix = new BitMatrix(33); - assertEquals(33, matrix.getDimension()); + assertEquals(33, matrix.getHeight()); for (int y = 0; y < 33; y++) { for (int x = 0; x < 33; x++) { if (y * x % 3 == 0) { @@ -94,7 +94,7 @@ public final class BitMatrixTestCase extends TestCase { public void testGetRow() { BitMatrix matrix = new BitMatrix(102, 5); for (int x = 0; x < 102; x++) { - if ((x & 3) == 0) { + if ((x & 0x03) == 0) { matrix.set(x, 2); } } @@ -114,7 +114,7 @@ public final class BitMatrixTestCase extends TestCase { assertEquals(200, array3.getSize()); for (int x = 0; x < 102; x++) { - boolean on = ((x & 3) == 0); + boolean on = ((x & 0x03) == 0); assertEquals(on, array.get(x)); assertEquals(on, array2.get(x)); assertEquals(on, array3.get(x)); diff --git a/core/test/src/com/google/zxing/oned/EAN13WriterTestCase.java b/core/test/src/com/google/zxing/oned/EAN13WriterTestCase.java index 7887ee4ed..f95bad462 100644 --- a/core/test/src/com/google/zxing/oned/EAN13WriterTestCase.java +++ b/core/test/src/com/google/zxing/oned/EAN13WriterTestCase.java @@ -18,7 +18,7 @@ package com.google.zxing.oned; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import junit.framework.TestCase; /** @@ -28,12 +28,9 @@ public final class EAN13WriterTestCase extends TestCase { public void testEncode() throws WriterException { String testStr = "00010100010110100111011001100100110111101001110101010110011011011001000010101110010011101000100101000"; - ByteMatrix result = new EAN13Writer().encode("5901234123457", BarcodeFormat.EAN_13, testStr.length(), 0); - byte[] row = result.getArray()[0]; - + BitMatrix result = new EAN13Writer().encode("5901234123457", BarcodeFormat.EAN_13, testStr.length(), 0); for (int i = 0; i < testStr.length(); i++) { - assertEquals("Element " + i, (Integer.parseInt(testStr.substring(i, - i + 1)) == 1) ? 0 : (byte) 255, row[i]); + assertEquals("Element " + i, testStr.charAt(i) == '1', result.get(i, 0)); } } diff --git a/core/test/src/com/google/zxing/oned/EAN8WriterTestCase.java b/core/test/src/com/google/zxing/oned/EAN8WriterTestCase.java index 7c7169433..653758356 100644 --- a/core/test/src/com/google/zxing/oned/EAN8WriterTestCase.java +++ b/core/test/src/com/google/zxing/oned/EAN8WriterTestCase.java @@ -18,7 +18,7 @@ package com.google.zxing.oned; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import junit.framework.TestCase; /** @@ -28,18 +28,11 @@ public final class EAN8WriterTestCase extends TestCase { public void testEncode() throws WriterException { String testStr = "0001010001011010111101111010110111010101001110111001010001001011100101000"; - ByteMatrix result = new EAN8Writer().encode("96385074", BarcodeFormat.EAN_8, testStr.length(), 0); - byte[] row = result.getArray()[0]; - /* - for (int i = 0; i < row.length; i++) { - System.out.print(row[i] + 1); - } - System.out.println(); - */ - + BitMatrix result = new EAN8Writer().encode("96385074", BarcodeFormat.EAN_8, testStr.length(), 0); for (int i = 0; i < testStr.length(); i++) { - assertEquals("Element " + i, (Integer.parseInt(testStr.substring(i, - i + 1)) == 1) ? 0 : (byte) 255, row[i]); + assertEquals("Element " + i, + testStr.charAt(i) == '1', + result.get(i, 0)); } } diff --git a/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java b/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java index e9ef9b1ac..da9b17d73 100644 --- a/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/QRCodeWriterTestCase.java @@ -19,7 +19,7 @@ package com.google.zxing.qrcode; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import junit.framework.TestCase; @@ -53,20 +53,22 @@ public final class QRCodeWriterTestCase extends TestCase { } // In case the golden images are not monochromatic, convert the RGB values to greyscale. - private static ByteMatrix createMatrixFromImage(BufferedImage image) { + private static BitMatrix createMatrixFromImage(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); int[] pixels = new int[width * height]; image.getRGB(0, 0, width, height, pixels, 0, width); - ByteMatrix matrix = new ByteMatrix(width, height); + BitMatrix matrix = new BitMatrix(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int pixel = pixels[y * width + x]; int luminance = (306 * ((pixel >> 16) & 0xFF) + 601 * ((pixel >> 8) & 0xFF) + 117 * (pixel & 0xFF)) >> 10; - matrix.set(x, y, luminance); + if (luminance <= 0x7F) { + matrix.set(x, y); + } } } return matrix; @@ -76,7 +78,7 @@ public final class QRCodeWriterTestCase extends TestCase { // The QR should be multiplied up to fit, with extra padding if necessary int bigEnough = 256; QRCodeWriter writer = new QRCodeWriter(); - ByteMatrix matrix = writer.encode("http://www.google.com/", BarcodeFormat.QR_CODE, bigEnough, + BitMatrix matrix = writer.encode("http://www.google.com/", BarcodeFormat.QR_CODE, bigEnough, bigEnough, null); assertNotNull(matrix); assertEquals(bigEnough, matrix.getWidth()); @@ -105,21 +107,18 @@ public final class QRCodeWriterTestCase extends TestCase { BufferedImage image = loadImage(fileName); assertNotNull(image); - ByteMatrix goldenResult = createMatrixFromImage(image); + BitMatrix goldenResult = createMatrixFromImage(image); assertNotNull(goldenResult); QRCodeWriter writer = new QRCodeWriter(); Hashtable hints = new Hashtable(); hints.put(EncodeHintType.ERROR_CORRECTION, ecLevel); - ByteMatrix generatedResult = writer.encode(contents, BarcodeFormat.QR_CODE, resolution, + BitMatrix generatedResult = writer.encode(contents, BarcodeFormat.QR_CODE, resolution, resolution, hints); - assertEquals("Width should be " + resolution + ", but was " + generatedResult.getWidth(), - resolution, generatedResult.getWidth()); - assertEquals("Height should be " + resolution + ", but was " + generatedResult.getHeight(), - resolution, generatedResult.getHeight()); - assertTrue("Expected " + goldenResult.toString() + " but got " + generatedResult.toString(), - Arrays.deepEquals(goldenResult.getArray(), generatedResult.getArray())); + assertEquals(resolution, generatedResult.getWidth()); + assertEquals(resolution, generatedResult.getHeight()); + assertEquals(goldenResult, generatedResult); } // Golden images are generated with "qrcode_sample.cc". The images are checked with both eye balls diff --git a/core/test/src/com/google/zxing/qrcode/encoder/BitVectorTestCase.java b/core/test/src/com/google/zxing/qrcode/encoder/BitVectorTestCase.java index 05abfd6f2..87cc4aea6 100644 --- a/core/test/src/com/google/zxing/qrcode/encoder/BitVectorTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/encoder/BitVectorTestCase.java @@ -16,6 +16,7 @@ package com.google.zxing.qrcode.encoder; +import com.google.zxing.common.BitArray; import junit.framework.TestCase; /** @@ -24,154 +25,154 @@ import junit.framework.TestCase; */ public class BitVectorTestCase extends TestCase { - private static int getUnsignedByte(BitVector v, int index) { - return v.getArray()[index] & 0xff; + private static long getUnsignedInt(BitArray v, int index) { + long result = 0L; + for (int i = 0, offset = index << 3; i < 32; i++) { + if (v.get(offset + i)) { + result |= 1L << (31 - i); + } + } + return result; } public void testAppendBit() { - BitVector v = new BitVector(); - assertEquals(0, v.sizeInBytes()); + BitArray v = new BitArray(); + assertEquals(0, v.getSizeInBytes()); // 1 - v.appendBit(1); - assertEquals(1, v.size()); - assertEquals(0x80, getUnsignedByte(v, 0)); + v.appendBit(true); + assertEquals(1, v.getSize()); + assertEquals(0x80000000L, getUnsignedInt(v, 0)); // 10 - v.appendBit(0); - assertEquals(2, v.size()); - assertEquals(0x80, getUnsignedByte(v, 0)); + v.appendBit(false); + assertEquals(2, v.getSize()); + assertEquals(0x80000000L, getUnsignedInt(v, 0)); // 101 - v.appendBit(1); - assertEquals(3, v.size()); - assertEquals(0xa0, getUnsignedByte(v, 0)); + v.appendBit(true); + assertEquals(3, v.getSize()); + assertEquals(0xa0000000L, getUnsignedInt(v, 0)); // 1010 - v.appendBit(0); - assertEquals(4, v.size()); - assertEquals(0xa0, getUnsignedByte(v, 0)); + v.appendBit(false); + assertEquals(4, v.getSize()); + assertEquals(0xa0000000L, getUnsignedInt(v, 0)); // 10101 - v.appendBit(1); - assertEquals(5, v.size()); - assertEquals(0xa8, getUnsignedByte(v, 0)); + v.appendBit(true); + assertEquals(5, v.getSize()); + assertEquals(0xa8000000L, getUnsignedInt(v, 0)); // 101010 - v.appendBit(0); - assertEquals(6, v.size()); - assertEquals(0xa8, getUnsignedByte(v, 0)); + v.appendBit(false); + assertEquals(6, v.getSize()); + assertEquals(0xa8000000L, getUnsignedInt(v, 0)); // 1010101 - v.appendBit(1); - assertEquals(7, v.size()); - assertEquals(0xaa, getUnsignedByte(v, 0)); + v.appendBit(true); + assertEquals(7, v.getSize()); + assertEquals(0xaa000000L, getUnsignedInt(v, 0)); // 10101010 - v.appendBit(0); - assertEquals(8, v.size()); - assertEquals(0xaa, getUnsignedByte(v, 0)); + v.appendBit(false); + assertEquals(8, v.getSize()); + assertEquals(0xaa000000L, getUnsignedInt(v, 0)); // 10101010 1 - v.appendBit(1); - assertEquals(9, v.size()); - assertEquals(0xaa, getUnsignedByte(v, 0)); - assertEquals(0x80, getUnsignedByte(v, 1)); + v.appendBit(true); + assertEquals(9, v.getSize()); + assertEquals(0xaa800000L, getUnsignedInt(v, 0)); // 10101010 10 - v.appendBit(0); - assertEquals(10, v.size()); - assertEquals(0xaa, getUnsignedByte(v, 0)); - assertEquals(0x80, getUnsignedByte(v, 1)); + v.appendBit(false); + assertEquals(10, v.getSize()); + assertEquals(0xaa800000L, getUnsignedInt(v, 0)); } public void testAppendBits() { { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0x1, 1); - assertEquals(1, v.size()); - assertEquals(0x80, getUnsignedByte(v, 0)); + assertEquals(1, v.getSize()); + assertEquals(0x80000000L, getUnsignedInt(v, 0)); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0xff, 8); - assertEquals(8, v.size()); - assertEquals(0xff, getUnsignedByte(v, 0)); + assertEquals(8, v.getSize()); + assertEquals(0xff000000L, getUnsignedInt(v, 0)); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0xff7, 12); - assertEquals(12, v.size()); - assertEquals(0xff, getUnsignedByte(v, 0)); - assertEquals(0x70, getUnsignedByte(v, 1)); + assertEquals(12, v.getSize()); + assertEquals(0xff700000L, getUnsignedInt(v, 0)); } } public void testNumBytes() { - BitVector v = new BitVector(); - assertEquals(0, v.sizeInBytes()); - v.appendBit(0); + BitArray v = new BitArray(); + assertEquals(0, v.getSizeInBytes()); + v.appendBit(false); // 1 bit was added in the vector, so 1 byte should be consumed. - assertEquals(1, v.sizeInBytes()); + assertEquals(1, v.getSizeInBytes()); v.appendBits(0, 7); - assertEquals(1, v.sizeInBytes()); + assertEquals(1, v.getSizeInBytes()); v.appendBits(0, 8); - assertEquals(2, v.sizeInBytes()); + assertEquals(2, v.getSizeInBytes()); v.appendBits(0, 1); // We now have 17 bits, so 3 bytes should be consumed. - assertEquals(3, v.sizeInBytes()); + assertEquals(3, v.getSizeInBytes()); } public void testAppendBitVector() { - BitVector v1 = new BitVector(); + BitArray v1 = new BitArray(); v1.appendBits(0xbe, 8); - BitVector v2 = new BitVector(); + BitArray v2 = new BitArray(); v2.appendBits(0xef, 8); - v1.appendBitVector(v2); + v1.appendBitArray(v2); // beef = 1011 1110 1110 1111 - assertEquals("1011111011101111", v1.toString()); + assertEquals(" X.XXXXX. XXX.XXXX", v1.toString()); } public void testXOR() { { - BitVector v1 = new BitVector(); + BitArray v1 = new BitArray(); v1.appendBits(0x5555aaaa, 32); - BitVector v2 = new BitVector(); + BitArray v2 = new BitArray(); v2.appendBits(0xaaaa5555, 32); v1.xor(v2); - assertEquals(0xff, getUnsignedByte(v1, 0)); - assertEquals(0xff, getUnsignedByte(v1, 1)); - assertEquals(0xff, getUnsignedByte(v1, 2)); - assertEquals(0xff, getUnsignedByte(v1, 3)); + assertEquals(0xffffffffL, getUnsignedInt(v1, 0)); } { - BitVector v1 = new BitVector(); + BitArray v1 = new BitArray(); v1.appendBits(0x2a, 7); // 010 1010 - BitVector v2 = new BitVector(); + BitArray v2 = new BitArray(); v2.appendBits(0x55, 7); // 101 0101 v1.xor(v2); - assertEquals(0xfe, getUnsignedByte(v1, 0)); // 1111 1110 + assertEquals(0xfe000000L, getUnsignedInt(v1, 0)); // 1111 1110 } } public void testAt() { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0xdead, 16); // 1101 1110 1010 1101 - assertEquals(1, v.at(0)); - assertEquals(1, v.at(1)); - assertEquals(0, v.at(2)); - assertEquals(1, v.at(3)); + assertTrue(v.get(0)); + assertTrue(v.get(1)); + assertFalse(v.get(2)); + assertTrue(v.get(3)); - assertEquals(1, v.at(4)); - assertEquals(1, v.at(5)); - assertEquals(1, v.at(6)); - assertEquals(0, v.at(7)); + assertTrue(v.get(4)); + assertTrue(v.get(5)); + assertTrue(v.get(6)); + assertFalse(v.get(7)); - assertEquals(1, v.at(8)); - assertEquals(0, v.at(9)); - assertEquals(1, v.at(10)); - assertEquals(0, v.at(11)); + assertTrue(v.get(8)); + assertFalse(v.get(9)); + assertTrue(v.get(10)); + assertFalse(v.get(11)); - assertEquals(1, v.at(12)); - assertEquals(1, v.at(13)); - assertEquals(0, v.at(14)); - assertEquals(1, v.at(15)); + assertTrue(v.get(12)); + assertTrue(v.get(13)); + assertFalse(v.get(14)); + assertTrue(v.get(15)); } public void testToString() { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0xdead, 16); // 1101 1110 1010 1101 - assertEquals("1101111010101101", v.toString()); + assertEquals(" XX.XXXX. X.X.XX.X", v.toString()); } } diff --git a/core/test/src/com/google/zxing/qrcode/encoder/EncoderTestCase.java b/core/test/src/com/google/zxing/qrcode/encoder/EncoderTestCase.java index 86e6200db..89912fad6 100644 --- a/core/test/src/com/google/zxing/qrcode/encoder/EncoderTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/encoder/EncoderTestCase.java @@ -17,7 +17,7 @@ package com.google.zxing.qrcode.encoder; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteArray; +import com.google.zxing.common.BitArray; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.Mode; import junit.framework.TestCase; @@ -60,28 +60,29 @@ public final class EncoderTestCase extends TestCase { public void testChooseMode() throws WriterException { // Numeric mode. - assertEquals(Mode.NUMERIC, Encoder.chooseMode("0")); - assertEquals(Mode.NUMERIC, Encoder.chooseMode("0123456789")); + assertSame(Mode.NUMERIC, Encoder.chooseMode("0")); + assertSame(Mode.NUMERIC, Encoder.chooseMode("0123456789")); // Alphanumeric mode. - assertEquals(Mode.ALPHANUMERIC, Encoder.chooseMode("A")); - assertEquals(Mode.ALPHANUMERIC, - Encoder.chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")); + assertSame(Mode.ALPHANUMERIC, Encoder.chooseMode("A")); + assertSame(Mode.ALPHANUMERIC, + Encoder.chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")); // 8-bit byte mode. - assertEquals(Mode.BYTE, Encoder.chooseMode("a")); - assertEquals(Mode.BYTE, Encoder.chooseMode("#")); - assertEquals(Mode.BYTE, Encoder.chooseMode("")); + assertSame(Mode.BYTE, Encoder.chooseMode("a")); + assertSame(Mode.BYTE, Encoder.chooseMode("#")); + assertSame(Mode.BYTE, Encoder.chooseMode("")); // Kanji mode. We used to use MODE_KANJI for these, but we stopped // doing that as we cannot distinguish Shift_JIS from other encodings // from data bytes alone. See also comments in qrcode_encoder.h. // AIUE in Hiragana in Shift_JIS - assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0x8,0xa,0x8,0xa,0x8,0xa,0x8,(byte)0xa6}))); + assertSame(Mode.BYTE, + Encoder.chooseMode(shiftJISString(new byte[]{0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, (byte) 0xa6}))); // Nihon in Kanji in Shift_JIS. - assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0x9,0xf,0x9,0x7b}))); + assertSame(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[]{0x9, 0xf, 0x9, 0x7b}))); // Sou-Utsu-Byou in Kanji in Shift_JIS. - assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0xe,0x4,0x9,0x5,0x9,0x61}))); + assertSame(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[]{0xe, 0x4, 0x9, 0x5, 0x9, 0x61}))); } public void testEncode() throws WriterException { @@ -126,43 +127,43 @@ public final class EncoderTestCase extends TestCase { } public void testAppendModeInfo() { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendModeInfo(Mode.NUMERIC, bits); - assertEquals("0001", bits.toString()); + assertEquals(" ...X", bits.toString()); } public void testAppendLengthInfo() throws WriterException { { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendLengthInfo(1, // 1 letter (1/1). 1, // version 1. Mode.NUMERIC, bits); - assertEquals("0000000001", bits.toString()); // 10 bits. + assertEquals(" ........ .X", bits.toString()); // 10 bits. } { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendLengthInfo(2, // 2 letters (2/1). 10, // version 10. Mode.ALPHANUMERIC, bits); - assertEquals("00000000010", bits.toString()); // 11 bits. + assertEquals(" ........ .X.", bits.toString()); // 11 bits. } { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendLengthInfo(255, // 255 letter (255/1). 27, // version 27. Mode.BYTE, bits); - assertEquals("0000000011111111", bits.toString()); // 16 bits. + assertEquals(" ........ XXXXXXXX", bits.toString()); // 16 bits. } { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendLengthInfo(512, // 512 letters (1024/2). 40, // version 40. Mode.KANJI, bits); - assertEquals("001000000000", bits.toString()); // 12 bits. + assertEquals(" ..X..... ....", bits.toString()); // 12 bits. } } @@ -170,16 +171,16 @@ public final class EncoderTestCase extends TestCase { { // Should use appendNumericBytes. // 1 = 01 = 0001 in 4 bits. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendBytes("1", Mode.NUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); - assertEquals("0001" , bits.toString()); + assertEquals(" ...X" , bits.toString()); } { // Should use appendAlphanumericBytes. // A = 10 = 0xa = 001010 in 6 bits - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendBytes("A", Mode.ALPHANUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); - assertEquals("001010" , bits.toString()); + assertEquals(" ..X.X." , bits.toString()); // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC. try { Encoder.appendBytes("a", Mode.ALPHANUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); @@ -190,60 +191,60 @@ public final class EncoderTestCase extends TestCase { { // Should use append8BitBytes. // 0x61, 0x62, 0x63 - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendBytes("abc", Mode.BYTE, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); - assertEquals("011000010110001001100011", bits.toString()); + assertEquals(" .XX....X .XX...X. .XX...XX", bits.toString()); // Anything can be encoded in QRCode.MODE_8BIT_BYTE. Encoder.appendBytes("\0", Mode.BYTE, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); } { // Should use appendKanjiBytes. // 0x93, 0x5f - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), Mode.KANJI, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); - assertEquals("0110110011111", bits.toString()); + assertEquals(" .XX.XX.. XXXXX", bits.toString()); } } public void testTerminateBits() throws WriterException { { - BitVector v = new BitVector(); + BitArray v = new BitArray(); Encoder.terminateBits(0, v); assertEquals("", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); Encoder.terminateBits(1, v); - assertEquals("00000000", v.toString()); + assertEquals(" ........", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0, 3); // Append 000 Encoder.terminateBits(1, v); - assertEquals("00000000", v.toString()); + assertEquals(" ........", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0, 5); // Append 00000 Encoder.terminateBits(1, v); - assertEquals("00000000", v.toString()); + assertEquals(" ........", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0, 8); // Append 00000000 Encoder.terminateBits(1, v); - assertEquals("00000000", v.toString()); + assertEquals(" ........", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); Encoder.terminateBits(2, v); - assertEquals("0000000011101100", v.toString()); + assertEquals(" ........ XXX.XX..", v.toString()); } { - BitVector v = new BitVector(); + BitArray v = new BitArray(); v.appendBits(0, 1); // Append 0 Encoder.terminateBits(3, v); - assertEquals("000000001110110000010001", v.toString()); + assertEquals(" ........ XXX.XX.. ...X...X", v.toString()); } } @@ -286,11 +287,11 @@ public final class EncoderTestCase extends TestCase { public void testInterleaveWithECBytes() throws WriterException { { byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236}; - BitVector in = new BitVector(); + BitArray in = new BitArray(); for (byte dataByte: dataBytes) { in.appendBits(dataByte, 8); } - BitVector out = new BitVector(); + BitArray out = new BitArray(); Encoder.interleaveWithECBytes(in, 26, 9, 1, out); byte[] expected = { // Data bytes. @@ -299,8 +300,9 @@ public final class EncoderTestCase extends TestCase { 42, (byte)159, 74, (byte)221, (byte)244, (byte)169, (byte)239, (byte)150, (byte)138, 70, (byte)237, 85, (byte)224, 96, 74, (byte)219, 61, }; - assertEquals(expected.length, out.sizeInBytes()); - byte[] outArray = out.getArray(); + assertEquals(expected.length, out.getSizeInBytes()); + byte[] outArray = new byte[expected.length]; + out.toBytes(0, outArray, 0, expected.length); // Can't use Arrays.equals(), because outArray may be longer than out.sizeInBytes() for (int x = 0; x < expected.length; x++) { assertEquals(expected[x], outArray[x]); @@ -316,11 +318,11 @@ public final class EncoderTestCase extends TestCase { (byte)135, (byte)151, (byte)160, (byte)236, 17, (byte)236, 17, (byte)236, 17, (byte)236, 17 }; - BitVector in = new BitVector(); + BitArray in = new BitArray(); for (byte dataByte: dataBytes) { in.appendBits(dataByte, 8); } - BitVector out = new BitVector(); + BitArray out = new BitArray(); Encoder.interleaveWithECBytes(in, 134, 62, 4, out); byte[] expected = { // Data bytes. @@ -339,8 +341,9 @@ public final class EncoderTestCase extends TestCase { (byte)140, 61, (byte)179, (byte)154, (byte)214, (byte)138, (byte)147, 87, 27, 96, 77, 47, (byte)187, 49, (byte)156, (byte)214, }; - assertEquals(expected.length, out.sizeInBytes()); - byte[] outArray = out.getArray(); + assertEquals(expected.length, out.getSizeInBytes()); + byte[] outArray = new byte[expected.length]; + out.toBytes(0, outArray, 0, expected.length); for (int x = 0; x < expected.length; x++) { assertEquals(expected[x], outArray[x]); } @@ -350,31 +353,31 @@ public final class EncoderTestCase extends TestCase { public void testAppendNumericBytes() { { // 1 = 01 = 0001 in 4 bits. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendNumericBytes("1", bits); - assertEquals("0001" , bits.toString()); + assertEquals(" ...X" , bits.toString()); } { // 12 = 0xc = 0001100 in 7 bits. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendNumericBytes("12", bits); - assertEquals("0001100" , bits.toString()); + assertEquals(" ...XX.." , bits.toString()); } { // 123 = 0x7b = 0001111011 in 10 bits. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendNumericBytes("123", bits); - assertEquals("0001111011" , bits.toString()); + assertEquals(" ...XXXX. XX" , bits.toString()); } { // 1234 = "123" + "4" = 0001111011 + 0100 - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendNumericBytes("1234", bits); - assertEquals("0001111011" + "0100" , bits.toString()); + assertEquals(" ...XXXX. XX.X.." , bits.toString()); } { // Empty. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendNumericBytes("", bits); assertEquals("" , bits.toString()); } @@ -383,31 +386,31 @@ public final class EncoderTestCase extends TestCase { public void testAppendAlphanumericBytes() throws WriterException { { // A = 10 = 0xa = 001010 in 6 bits - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendAlphanumericBytes("A", bits); - assertEquals("001010" , bits.toString()); + assertEquals(" ..X.X." , bits.toString()); } { // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendAlphanumericBytes("AB", bits); - assertEquals("00111001101", bits.toString()); + assertEquals(" ..XXX..X X.X", bits.toString()); } { // ABC = "AB" + "C" = 00111001101 + 001100 - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendAlphanumericBytes("ABC", bits); - assertEquals("00111001101" + "001100" , bits.toString()); + assertEquals(" ..XXX..X X.X..XX. ." , bits.toString()); } { // Empty. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendAlphanumericBytes("", bits); assertEquals("" , bits.toString()); } { // Invalid data. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); try { Encoder.appendAlphanumericBytes("abc", bits); } catch (WriterException we) { @@ -419,13 +422,13 @@ public final class EncoderTestCase extends TestCase { public void testAppend8BitBytes() throws WriterException { { // 0x61, 0x62, 0x63 - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.append8BitBytes("abc", bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); - assertEquals("01100001" + "01100010" + "01100011", bits.toString()); + assertEquals(" .XX....X .XX...X. .XX...XX", bits.toString()); } { // Empty. - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.append8BitBytes("", bits, Encoder.DEFAULT_BYTE_MODE_ENCODING); assertEquals("", bits.toString()); } @@ -433,11 +436,11 @@ public final class EncoderTestCase extends TestCase { // Numbers are from page 21 of JISX0510:2004 public void testAppendKanjiBytes() throws WriterException { - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), bits); - assertEquals("0110110011111", bits.toString()); + assertEquals(" .XX.XX.. XXXXX", bits.toString()); Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0xe4,(byte)0xaa}), bits); - assertEquals("0110110011111" + "1101010101010", bits.toString()); + assertEquals(" .XX.XX.. XXXXXXX. X.X.X.X. X.", bits.toString()); } // Numbers are from http://www.swetake.com/qr/qr3.html and @@ -445,37 +448,37 @@ public final class EncoderTestCase extends TestCase { public void testGenerateECBytes() { { byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236}; - ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 17); + byte[] ecBytes = Encoder.generateECBytes(dataBytes, 17); int[] expected = { 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61 }; - assertEquals(expected.length, ecBytes.size()); + assertEquals(expected.length, ecBytes.length); for (int x = 0; x < expected.length; x++) { - assertEquals(expected[x], ecBytes.at(x)); + assertEquals(expected[x], ecBytes[x] & 0xFF); } } { byte[] dataBytes = {67, 70, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166, (byte)182, (byte)198, (byte)214}; - ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 18); + byte[] ecBytes = Encoder.generateECBytes(dataBytes, 18); int[] expected = { 175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187 }; - assertEquals(expected.length, ecBytes.size()); + assertEquals(expected.length, ecBytes.length); for (int x = 0; x < expected.length; x++) { - assertEquals(expected[x], ecBytes.at(x)); + assertEquals(expected[x], ecBytes[x] & 0xFF); } } { - // High-order zero cofficient case. + // High-order zero coefficient case. byte[] dataBytes = {32, 49, (byte)205, 69, 42, 20, 0, (byte)236, 17}; - ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 17); + byte[] ecBytes = Encoder.generateECBytes(dataBytes, 17); int[] expected = { 0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213 }; - assertEquals(expected.length, ecBytes.size()); + assertEquals(expected.length, ecBytes.length); for (int x = 0; x < expected.length; x++) { - assertEquals(expected[x], ecBytes.at(x)); + assertEquals(expected[x], ecBytes[x] & 0xFF); } } } diff --git a/core/test/src/com/google/zxing/qrcode/encoder/MaskUtilTestCase.java b/core/test/src/com/google/zxing/qrcode/encoder/MaskUtilTestCase.java index 51790a8eb..183cdb1f1 100644 --- a/core/test/src/com/google/zxing/qrcode/encoder/MaskUtilTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/encoder/MaskUtilTestCase.java @@ -16,7 +16,6 @@ package com.google.zxing.qrcode.encoder; -import com.google.zxing.common.ByteMatrix; import junit.framework.TestCase; /** diff --git a/core/test/src/com/google/zxing/qrcode/encoder/MatrixUtilTestCase.java b/core/test/src/com/google/zxing/qrcode/encoder/MatrixUtilTestCase.java index 12becfcfe..87336dc1f 100644 --- a/core/test/src/com/google/zxing/qrcode/encoder/MatrixUtilTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/encoder/MatrixUtilTestCase.java @@ -17,7 +17,7 @@ package com.google.zxing.qrcode.encoder; import com.google.zxing.WriterException; -import com.google.zxing.common.ByteMatrix; +import com.google.zxing.common.BitArray; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import junit.framework.TestCase; @@ -202,7 +202,7 @@ public final class MatrixUtilTestCase extends TestCase { " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); ByteMatrix matrix = new ByteMatrix(21, 21); MatrixUtil.clearMatrix(matrix); MatrixUtil.embedBasicPatterns(1, matrix); @@ -237,7 +237,7 @@ public final class MatrixUtilTestCase extends TestCase { char[] bytes = {32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61}; - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); for (char c: bytes) { bits.appendBits(c, 8); } @@ -280,18 +280,18 @@ public final class MatrixUtilTestCase extends TestCase { // tested them in TEST(calculateBCHCode). public void testMakeVersionInfoBits() throws WriterException { // From Appendix D in JISX0510:2004 (p 68) - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); MatrixUtil.makeVersionInfoBits(7, bits); - assertEquals("000111110010010100", bits.toString()); + assertEquals(" ...XXXXX ..X..X.X ..", bits.toString()); } // We don't test a lot of cases in this function since we've already // tested them in TEST(calculateBCHCode). public void testMakeTypeInfoInfoBits() throws WriterException { // From Appendix C in JISX0510:2004 (p 65) - BitVector bits = new BitVector(); + BitArray bits = new BitArray(); MatrixUtil.makeTypeInfoBits(ErrorCorrectionLevel.M, 5, bits); - assertEquals("100000011001110", bits.toString()); + assertEquals(" X......X X..XXX.", bits.toString()); } } diff --git a/core/test/src/com/google/zxing/qrcode/encoder/QRCodeTestCase.java b/core/test/src/com/google/zxing/qrcode/encoder/QRCodeTestCase.java index e17cf80c6..3e25d7636 100644 --- a/core/test/src/com/google/zxing/qrcode/encoder/QRCodeTestCase.java +++ b/core/test/src/com/google/zxing/qrcode/encoder/QRCodeTestCase.java @@ -16,7 +16,6 @@ package com.google.zxing.qrcode.encoder; -import com.google.zxing.common.ByteMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.Mode; import junit.framework.TestCase; diff --git a/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java b/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java index 3c82a56dd..918455510 100644 --- a/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java +++ b/javase/src/com/google/zxing/client/j2se/MatrixToImageWriter.java @@ -17,7 +17,6 @@ package com.google.zxing.client.j2se; import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.ByteMatrix; import javax.imageio.ImageIO; import java.io.File; @@ -26,7 +25,7 @@ import java.io.IOException; import java.awt.image.BufferedImage; /** - * Writes a {@link BitMatrix} or {@link ByteMatrix} to {@link BufferedImage}, + * Writes a {@link BitMatrix} to {@link BufferedImage}, * file or stream. Provided here instead of core since it depends on * Java SE libraries. * @@ -55,25 +54,6 @@ public final class MatrixToImageWriter { return image; } - /** - * Renders a {@link ByteMatrix} as an image, as a - * {@link BufferedImage}. The byte values are construed as (unsigned) - * luminance values, in theory. - * However, anything but 0 will be rendered as white, and 0 will be - * rendered as black. - */ - public static BufferedImage toBufferedImage(ByteMatrix matrix) { - int width = matrix.getWidth(); - int height = matrix.getHeight(); - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - image.setRGB(x, y, matrix.get(x, y) == 0 ? BLACK : WHITE); - } - } - return image; - } - /** * Writes a {@link BitMatrix} to a file. * @@ -85,17 +65,6 @@ public final class MatrixToImageWriter { ImageIO.write(image, format, file); } - /** - * Writes a {@link ByteMatrix} to a file. - * - * @see #toBufferedImage(ByteMatrix) - */ - public static void writeToFile(ByteMatrix matrix, String format, File file) - throws IOException { - BufferedImage image = toBufferedImage(matrix); - ImageIO.write(image, format, file); - } - /** * Writes a {@link BitMatrix} to a stream. * @@ -107,15 +76,4 @@ public final class MatrixToImageWriter { ImageIO.write(image, format, stream); } - /** - * Writes a {@link ByteMatrix} to a stream. - * - * @see #toBufferedImage(ByteMatrix) - */ - public static void writeToStream(ByteMatrix matrix, String format, OutputStream stream) - throws IOException { - BufferedImage image = toBufferedImage(matrix); - ImageIO.write(image, format, stream); - } - }