mirror of
https://github.com/zxing/zxing.git
synced 2024-11-10 04:54:04 -08:00
Rewrote BitMatrix to allow rectangular 2D arrays, and so that every row begins with a new int, which makes it fast to copy out rows into BitArrays. This will be the basis of the upcoming bitmap refactoring for 1D Readers.
git-svn-id: https://zxing.googlecode.com/svn/trunk@978 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
7fec06465a
commit
b613112615
|
@ -17,12 +17,15 @@
|
||||||
package com.google.zxing.common;
|
package com.google.zxing.common;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Represents a square matrix of bits. In function arguments below, and throughout the common
|
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
|
||||||
* module, x is the column position, and y is the row position. The ordering is always x, y.
|
* module, x is the column position, and y is the row position. The ordering is always x, y.
|
||||||
* The origin is at the top-left.</p>
|
* The origin is at the top-left.</p>
|
||||||
*
|
*
|
||||||
* <p>Internally the bits are represented in a compact 1-D array of 32-bit ints.
|
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
|
||||||
* The ordering of bits is row-major. Within each int, the least significant bits are used first,
|
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
|
||||||
|
* efficiently.</p>
|
||||||
|
*
|
||||||
|
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
|
||||||
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
|
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
|
||||||
*
|
*
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
|
@ -30,20 +33,29 @@ package com.google.zxing.common;
|
||||||
*/
|
*/
|
||||||
public final class BitMatrix {
|
public final class BitMatrix {
|
||||||
|
|
||||||
private final int dimension;
|
// TODO: Just like BitArray, these need to be public so ProGuard can inline them.
|
||||||
private final int[] bits;
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
public final int rowSize;
|
||||||
|
public final int[] bits;
|
||||||
|
|
||||||
|
// A helper to construct a square matrix.
|
||||||
public BitMatrix(int dimension) {
|
public BitMatrix(int dimension) {
|
||||||
if (dimension < 1) {
|
this(dimension, dimension);
|
||||||
throw new IllegalArgumentException("dimension must be at least 1");
|
}
|
||||||
|
|
||||||
|
public BitMatrix(int width, int height) {
|
||||||
|
if (width < 1 || height < 1) {
|
||||||
|
throw new IllegalArgumentException("Both dimensions must be greater than 0");
|
||||||
}
|
}
|
||||||
this.dimension = dimension;
|
this.width = width;
|
||||||
int numBits = dimension * dimension;
|
this.height = height;
|
||||||
int arraySize = numBits >> 5; // one int per 32 bits
|
int rowSize = width >> 5;
|
||||||
if ((numBits & 0x1F) != 0) { // plus one more if there are leftovers
|
if ((width & 0x1f) != 0) {
|
||||||
arraySize++;
|
rowSize++;
|
||||||
}
|
}
|
||||||
bits = new int[arraySize];
|
this.rowSize = rowSize;
|
||||||
|
bits = new int[rowSize * height];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,8 +66,8 @@ public final class BitMatrix {
|
||||||
* @return value of given bit in matrix
|
* @return value of given bit in matrix
|
||||||
*/
|
*/
|
||||||
public boolean get(int x, int y) {
|
public boolean get(int x, int y) {
|
||||||
int offset = y * dimension + x;
|
int offset = y * rowSize + (x >> 5);
|
||||||
return ((bits[offset >> 5] >>> (offset & 0x1F)) & 0x01) != 0;
|
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,8 +77,8 @@ public final class BitMatrix {
|
||||||
* @param y The vertical component (i.e. which row)
|
* @param y The vertical component (i.e. which row)
|
||||||
*/
|
*/
|
||||||
public void set(int x, int y) {
|
public void set(int x, int y) {
|
||||||
int offset = y * dimension + x;
|
int offset = y * rowSize + (x >> 5);
|
||||||
bits[offset >> 5] |= 1 << (offset & 0x1F);
|
bits[offset] |= 1 << (x & 0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,8 +88,8 @@ public final class BitMatrix {
|
||||||
* @param y The vertical component (i.e. which row)
|
* @param y The vertical component (i.e. which row)
|
||||||
*/
|
*/
|
||||||
public void flip(int x, int y) {
|
public void flip(int x, int y) {
|
||||||
int offset = y * dimension + x;
|
int offset = y * rowSize + (x >> 5);
|
||||||
bits[offset >> 5] ^= 1 << (offset & 0x1F);
|
bits[offset] ^= 1 << (x & 0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,37 +102,74 @@ public final class BitMatrix {
|
||||||
*/
|
*/
|
||||||
public void setRegion(int left, int top, int width, int height) {
|
public void setRegion(int left, int top, int width, int height) {
|
||||||
if (top < 0 || left < 0) {
|
if (top < 0 || left < 0) {
|
||||||
throw new IllegalArgumentException("left and top must be nonnegative");
|
throw new IllegalArgumentException("Left and top must be nonnegative");
|
||||||
}
|
}
|
||||||
if (height < 1 || width < 1) {
|
if (height < 1 || width < 1) {
|
||||||
throw new IllegalArgumentException("height and width must be at least 1");
|
throw new IllegalArgumentException("Height and width must be at least 1");
|
||||||
}
|
}
|
||||||
int right = left + width;
|
int right = left + width;
|
||||||
int bottom = top + height;
|
int bottom = top + height;
|
||||||
if (bottom > dimension || right > dimension) {
|
if (bottom > this.height || right > this.width) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException("The region must fit inside the matrix");
|
||||||
"top + height and left + width must be <= matrix dimension");
|
|
||||||
}
|
}
|
||||||
for (int y = top; y < bottom; y++) {
|
for (int y = top; y < bottom; y++) {
|
||||||
int yoffset = dimension * y;
|
int offset = y * rowSize;
|
||||||
for (int x = left; x < right; x++) {
|
for (int x = left; x < right; x++) {
|
||||||
int xoffset = yoffset + x;
|
bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
|
||||||
bits[xoffset >> 5] |= 1 << (xoffset & 0x1F);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A fast method to retrieve one row of data from the matrix as a BitArray.
|
||||||
|
*
|
||||||
|
* @param y The row to retrieve
|
||||||
|
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
|
||||||
|
* @return The resulting BitArray - this reference should always be used even when passing
|
||||||
|
* your own row
|
||||||
|
*/
|
||||||
|
BitArray getRow(int y, BitArray row) {
|
||||||
|
if (row == null || row.getSize() < width) {
|
||||||
|
row = new BitArray(width);
|
||||||
|
}
|
||||||
|
int offset = y * rowSize;
|
||||||
|
for (int x = 0; x < rowSize; x++) {
|
||||||
|
row.setBulk(x * 32, bits[offset + x]);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The width of the matrix
|
||||||
|
*/
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The height of the matrix
|
||||||
|
*/
|
||||||
|
public int getHeight() {
|
||||||
|
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
|
* @return row/column dimension of this matrix
|
||||||
*/
|
*/
|
||||||
public int getDimension() {
|
public int getDimension() {
|
||||||
return dimension;
|
if (width != height) {
|
||||||
|
throw new RuntimeException("Can't call getDimension() on a non-square matrix");
|
||||||
|
}
|
||||||
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer result = new StringBuffer(dimension * (dimension + 1));
|
StringBuffer result = new StringBuffer(height * (width + 1));
|
||||||
for (int y = 0; y < dimension; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < dimension; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
result.append(get(x, y) ? "X " : " ");
|
result.append(get(x, y) ? "X " : " ");
|
||||||
}
|
}
|
||||||
result.append('\n');
|
result.append('\n');
|
||||||
|
|
|
@ -20,22 +20,23 @@ import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
public final class BitMatrixTestCase extends TestCase {
|
public final class BitMatrixTestCase extends TestCase {
|
||||||
|
|
||||||
public void testGetSet() {
|
public void testGetSet() {
|
||||||
BitMatrix matrix = new BitMatrix(33);
|
BitMatrix matrix = new BitMatrix(33);
|
||||||
assertEquals(33, matrix.getDimension());
|
assertEquals(33, matrix.getDimension());
|
||||||
for (int i = 0; i < 33; i++) {
|
for (int y = 0; y < 33; y++) {
|
||||||
for (int j = 0; j < 33; j++) {
|
for (int x = 0; x < 33; x++) {
|
||||||
if (i * j % 3 == 0) {
|
if (y * x % 3 == 0) {
|
||||||
matrix.set(j, i);
|
matrix.set(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 33; i++) {
|
for (int y = 0; y < 33; y++) {
|
||||||
for (int j = 0; j < 33; j++) {
|
for (int x = 0; x < 33; x++) {
|
||||||
assertEquals(i * j % 3 == 0, matrix.get(j, i));
|
assertEquals(y * x % 3 == 0, matrix.get(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +44,81 @@ public final class BitMatrixTestCase extends TestCase {
|
||||||
public void testSetRegion() {
|
public void testSetRegion() {
|
||||||
BitMatrix matrix = new BitMatrix(5);
|
BitMatrix matrix = new BitMatrix(5);
|
||||||
matrix.setRegion(1, 1, 3, 3);
|
matrix.setRegion(1, 1, 3, 3);
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int y = 0; y < 5; y++) {
|
||||||
for (int j = 0; j < 5; j++) {
|
for (int x = 0; x < 5; x++) {
|
||||||
assertEquals(i >= 1 && i <= 3 && j >= 1 && j <= 3, matrix.get(j, i));
|
assertEquals(y >= 1 && y <= 3 && x >= 1 && x <= 3, matrix.get(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public void testRectangularMatrix() {
|
||||||
|
BitMatrix matrix = new BitMatrix(75, 20);
|
||||||
|
assertEquals(75, matrix.getWidth());
|
||||||
|
assertEquals(20, matrix.getHeight());
|
||||||
|
matrix.set(10, 0);
|
||||||
|
matrix.set(11, 1);
|
||||||
|
matrix.set(50, 2);
|
||||||
|
matrix.set(51, 3);
|
||||||
|
matrix.flip(74, 4);
|
||||||
|
matrix.flip(0, 5);
|
||||||
|
|
||||||
|
// Should all be on
|
||||||
|
assertTrue(matrix.get(10, 0));
|
||||||
|
assertTrue(matrix.get(11, 1));
|
||||||
|
assertTrue(matrix.get(50, 2));
|
||||||
|
assertTrue(matrix.get(51, 3));
|
||||||
|
assertTrue(matrix.get(74, 4));
|
||||||
|
assertTrue(matrix.get(0, 5));
|
||||||
|
|
||||||
|
// Flip a couple back off
|
||||||
|
matrix.flip(50, 2);
|
||||||
|
matrix.flip(51, 3);
|
||||||
|
assertFalse(matrix.get(50, 2));
|
||||||
|
assertFalse(matrix.get(51, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRectangularSetRegion() {
|
||||||
|
BitMatrix matrix = new BitMatrix(320, 240);
|
||||||
|
assertEquals(320, matrix.getWidth());
|
||||||
|
assertEquals(240, matrix.getHeight());
|
||||||
|
matrix.setRegion(105, 22, 80, 12);
|
||||||
|
|
||||||
|
// Only bits in the region should be on
|
||||||
|
for (int y = 0; y < 240; y++) {
|
||||||
|
for (int x = 0; x < 320; x++) {
|
||||||
|
assertEquals(y >= 22 && y < 34 && x >= 105 && x < 185, matrix.get(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetRow() {
|
||||||
|
BitMatrix matrix = new BitMatrix(102, 5);
|
||||||
|
for (int x = 0; x < 102; x++) {
|
||||||
|
if ((x & 3) == 0) {
|
||||||
|
matrix.set(x, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should allocate
|
||||||
|
BitArray array = matrix.getRow(2, null);
|
||||||
|
assertEquals(102, array.getSize());
|
||||||
|
|
||||||
|
// Should reallocate
|
||||||
|
BitArray array2 = new BitArray(60);
|
||||||
|
array2 = matrix.getRow(2, array2);
|
||||||
|
assertEquals(102, array2.getSize());
|
||||||
|
|
||||||
|
// Should use provided object, with original BitArray size
|
||||||
|
BitArray array3 = new BitArray(200);
|
||||||
|
array3 = matrix.getRow(2, array3);
|
||||||
|
assertEquals(200, array3.getSize());
|
||||||
|
|
||||||
|
for (int x = 0; x < 102; x++) {
|
||||||
|
boolean on = ((x & 3) == 0);
|
||||||
|
assertEquals(on, array.get(x));
|
||||||
|
assertEquals(on, array2.get(x));
|
||||||
|
assertEquals(on, array3.get(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue