* A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
* unsigned container, it's up to you to do byteValue & 0xff at each location.
*
- * JAVAPORT: I'm not happy about the argument ordering throughout the file, as I always like to have
- * the horizontal component first, but this is for compatibility with the C++ code. The original
- * code was a 2D array of ints, but since it only ever gets assigned -1, 0, and 1, I'm going to use
- * less memory and go with bytes.
+ * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
+ * -1, 0, and 1, I'm going to use less memory and go with bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ByteMatrix {
private final byte[][] bytes;
- private final int height;
private final int width;
+ private final int height;
- public ByteMatrix(int height, int width) {
+ public ByteMatrix(int width, int height) {
bytes = new byte[height][width];
- this.height = height;
this.width = width;
+ this.height = height;
}
public int height() {
return width;
}
- public byte get(int y, int x) {
+ public byte get(int x, int y) {
return bytes[y][x];
}
return bytes;
}
- public void set(int y, int x, byte value) {
+ public void set(int x, int y, byte value) {
bytes[y][x] = value;
}
- public void set(int y, int x, int value) {
+ public void set(int x, int y, int value) {
bytes[y][x] = (byte) value;
}
int multiple = outputWidth / fullWidth;
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
- ByteMatrix output = new ByteMatrix(outputHeight, outputWidth);
+ ByteMatrix output = new ByteMatrix(outputWidth, outputHeight);
byte[][] outputArray = output.getArray();
byte[] row = new byte[outputWidth];
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
- ByteMatrix output = new ByteMatrix(outputHeight, outputWidth);
+ ByteMatrix output = new ByteMatrix(outputWidth, outputHeight);
byte[][] outputArray = output.getArray();
// We could be tricky and use the first row in each set of multiple as the temporary storage,
// Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
int x1 = TYPE_INFO_COORDINATES[i][0];
int y1 = TYPE_INFO_COORDINATES[i][1];
- matrix.set(y1, x1, bit);
+ matrix.set(x1, y1, bit);
if (i < 8) {
// Right top corner.
int x2 = matrix.width() - i - 1;
int y2 = 8;
- matrix.set(y2, x2, bit);
+ matrix.set(x2, y2, bit);
} else {
// Left bottom corner.
int x2 = 8;
int y2 = matrix.height() - 7 + (i - 8);
- matrix.set(y2, x2, bit);
+ matrix.set(x2, y2, bit);
}
}
}
int bit = versionInfoBits.at(bitIndex);
bitIndex--;
// Left bottom corner.
- matrix.set(matrix.height() - 11 + j, i, bit);
- // Right bottom corner.
matrix.set(i, matrix.height() - 11 + j, bit);
+ // Right bottom corner.
+ matrix.set(matrix.height() - 11 + j, i, bit);
}
}
}
for (int i = 0; i < 2; ++i) {
int xx = x - i;
// Skip the cell if it's not empty.
- if (!isEmpty(matrix.get(y, xx))) {
+ if (!isEmpty(matrix.get(xx, y))) {
continue;
}
int bit;
bit ^= 0x1;
}
}
- matrix.set(y, xx, bit);
+ matrix.set(xx, y, bit);
}
y += direction;
}
for (int i = 8; i < matrix.width() - 8; ++i) {
int bit = (i + 1) % 2;
// Horizontal line.
- if (!isValidValue(matrix.get(6, i))) {
+ if (!isValidValue(matrix.get(i, 6))) {
throw new WriterException();
}
- if (isEmpty(matrix.get(6, i))) {
- matrix.set(6, i, bit);
+ if (isEmpty(matrix.get(i, 6))) {
+ matrix.set(i, 6, bit);
}
// Vertical line.
- if (!isValidValue(matrix.get(i, 6))) {
+ if (!isValidValue(matrix.get(6, i))) {
throw new WriterException();
}
- if (isEmpty(matrix.get(i, 6))) {
- matrix.set(i, 6, bit);
+ if (isEmpty(matrix.get(6, i))) {
+ matrix.set(6, i, bit);
}
}
}
// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
- if (matrix.get(matrix.height() - 8, 8) == 0) {
+ if (matrix.get(8, matrix.height() - 8) == 0) {
throw new WriterException();
}
- matrix.set(matrix.height() - 8, 8, 1);
+ matrix.set(8, matrix.height() - 8, 1);
}
private static void embedHorizontalSeparationPattern(int xStart, int yStart,
throw new WriterException("Bad horizontal separation pattern");
}
for (int x = 0; x < 8; ++x) {
- if (!isEmpty(matrix.get(yStart, xStart + x))) {
+ if (!isEmpty(matrix.get(xStart + x, yStart))) {
throw new WriterException();
}
- matrix.set(yStart, xStart + x, HORIZONTAL_SEPARATION_PATTERN[0][x]);
+ matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
}
}
throw new WriterException("Bad vertical separation pattern");
}
for (int y = 0; y < 7; ++y) {
- if (!isEmpty(matrix.get(yStart + y, xStart))) {
+ if (!isEmpty(matrix.get(xStart, yStart + y))) {
throw new WriterException();
}
- matrix.set(yStart + y, xStart, VERTICAL_SEPARATION_PATTERN[y][0]);
+ matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
}
}
}
for (int y = 0; y < 5; ++y) {
for (int x = 0; x < 5; ++x) {
- if (!isEmpty(matrix.get(yStart + y, xStart + x))) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
- matrix.set(yStart + y, xStart + x, POSITION_ADJUSTMENT_PATTERN[y][x]);
+ matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
}
}
}
}
for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 7; ++x) {
- if (!isEmpty(matrix.get(yStart + y, xStart + x))) {
+ if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
- matrix.set(yStart + y, xStart + x, POSITION_DETECTION_PATTERN[y][x]);
+ matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
}
}
}
continue;
}
// If the cell is unset, we embed the position adjustment pattern here.
- if (isEmpty(matrix.get(y, x))) {
+ if (isEmpty(matrix.get(x, y))) {
// -2 is necessary since the x/y coordinates point to the center of the pattern, not the
// left top corner.
embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
// call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
public int at(int x, int y) {
// The value must be zero or one.
- int value = matrix.get(y, x);
+ int value = matrix.get(x, y);
if (!(value == 0 || value == 1)) {
// this is really like an assert... not sure what better exception to use?
throw new RuntimeException("Bad value");
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
- ByteMatrix matrix = new ByteMatrix(height, width);
+ ByteMatrix matrix = new ByteMatrix(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(y, x, luminance);
+ matrix.set(x, y, luminance);
}
}
return matrix;
public final class MaskUtilTestCase extends TestCase {
public void testApplyMaskPenaltyRule1() {
{
- ByteMatrix matrix = new ByteMatrix(1, 4);
+ ByteMatrix matrix = new ByteMatrix(4, 1);
matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
- matrix.set(0, 2, 0);
- matrix.set(0, 3, 0);
+ matrix.set(1, 0, 0);
+ matrix.set(2, 0, 0);
+ matrix.set(3, 0, 0);
assertEquals(0, MaskUtil.applyMaskPenaltyRule1(matrix));
}
{ // Horizontal.
- ByteMatrix matrix = new ByteMatrix(1, 6);
- matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
- matrix.set(0, 2, 0);
- matrix.set(0, 3, 0);
- matrix.set(0, 4, 0);
- matrix.set(0, 5, 1);
- assertEquals(3, MaskUtil.applyMaskPenaltyRule1(matrix));
- matrix.set(0, 5, 0);
- assertEquals(4, MaskUtil.applyMaskPenaltyRule1(matrix));
- }
- { // Vertical.
ByteMatrix matrix = new ByteMatrix(6, 1);
matrix.set(0, 0, 0);
matrix.set(1, 0, 0);
matrix.set(5, 0, 0);
assertEquals(4, MaskUtil.applyMaskPenaltyRule1(matrix));
}
+ { // Vertical.
+ ByteMatrix matrix = new ByteMatrix(1, 6);
+ matrix.set(0, 0, 0);
+ matrix.set(0, 1, 0);
+ matrix.set(0, 2, 0);
+ matrix.set(0, 3, 0);
+ matrix.set(0, 4, 0);
+ matrix.set(0, 5, 1);
+ assertEquals(3, MaskUtil.applyMaskPenaltyRule1(matrix));
+ matrix.set(0, 5, 0);
+ assertEquals(4, MaskUtil.applyMaskPenaltyRule1(matrix));
+ }
}
public void testApplyMaskPenaltyRule2() {
{
ByteMatrix matrix = new ByteMatrix(2, 2);
matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
matrix.set(1, 0, 0);
+ matrix.set(0, 1, 0);
matrix.set(1, 1, 1);
assertEquals(0, MaskUtil.applyMaskPenaltyRule2(matrix));
}
{
ByteMatrix matrix = new ByteMatrix(2, 2);
matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
matrix.set(1, 0, 0);
+ matrix.set(0, 1, 0);
matrix.set(1, 1, 0);
assertEquals(3, MaskUtil.applyMaskPenaltyRule2(matrix));
}
{
ByteMatrix matrix = new ByteMatrix(3, 3);
matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
- matrix.set(0, 2, 0);
matrix.set(1, 0, 0);
- matrix.set(1, 1, 0);
- matrix.set(1, 2, 0);
matrix.set(2, 0, 0);
+ matrix.set(0, 1, 0);
+ matrix.set(1, 1, 0);
matrix.set(2, 1, 0);
+ matrix.set(0, 2, 0);
+ matrix.set(1, 2, 0);
matrix.set(2, 2, 0);
// Four instances of 2x2 blocks.
assertEquals(3 * 4, MaskUtil.applyMaskPenaltyRule2(matrix));
public void testApplyMaskPenaltyRule3() {
{
// Horizontal 00001011101.
- ByteMatrix matrix = new ByteMatrix(1, 11);
- matrix.set(0, 0, 0);
- matrix.set(0, 1, 0);
- matrix.set(0, 2, 0);
- matrix.set(0, 3, 0);
- matrix.set(0, 4, 1);
- matrix.set(0, 5, 0);
- matrix.set(0, 6, 1);
- matrix.set(0, 7, 1);
- matrix.set(0, 8, 1);
- matrix.set(0, 9, 0);
- matrix.set(0, 10, 1);
- assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
- }
- {
- // Horizontal 10111010000.
- ByteMatrix matrix = new ByteMatrix(1, 11);
- matrix.set(0, 0, 1);
- matrix.set(0, 1, 0);
- matrix.set(0, 2, 1);
- matrix.set(0, 3, 1);
- matrix.set(0, 4, 1);
- matrix.set(0, 5, 0);
- matrix.set(0, 6, 1);
- matrix.set(0, 7, 0);
- matrix.set(0, 8, 0);
- matrix.set(0, 9, 0);
- matrix.set(0, 10, 0);
- assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
- }
- {
- // Vertical 00001011101.
ByteMatrix matrix = new ByteMatrix(11, 1);
matrix.set(0, 0, 0);
matrix.set(1, 0, 0);
assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
}
{
- // Vertical 10111010000.
+ // Horizontal 10111010000.
ByteMatrix matrix = new ByteMatrix(11, 1);
matrix.set(0, 0, 1);
matrix.set(1, 0, 0);
matrix.set(10, 0, 0);
assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
}
+ {
+ // Vertical 00001011101.
+ ByteMatrix matrix = new ByteMatrix(1, 11);
+ matrix.set(0, 0, 0);
+ matrix.set(0, 1, 0);
+ matrix.set(0, 2, 0);
+ matrix.set(0, 3, 0);
+ matrix.set(0, 4, 1);
+ matrix.set(0, 5, 0);
+ matrix.set(0, 6, 1);
+ matrix.set(0, 7, 1);
+ matrix.set(0, 8, 1);
+ matrix.set(0, 9, 0);
+ matrix.set(0, 10, 1);
+ assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
+ }
+ {
+ // Vertical 10111010000.
+ ByteMatrix matrix = new ByteMatrix(1, 11);
+ matrix.set(0, 0, 1);
+ matrix.set(0, 1, 0);
+ matrix.set(0, 2, 1);
+ matrix.set(0, 3, 1);
+ matrix.set(0, 4, 1);
+ matrix.set(0, 5, 0);
+ matrix.set(0, 6, 1);
+ matrix.set(0, 7, 0);
+ matrix.set(0, 8, 0);
+ matrix.set(0, 9, 0);
+ matrix.set(0, 10, 0);
+ assertEquals(40, MaskUtil.applyMaskPenaltyRule3(matrix));
+ }
}
public void testApplyMaskPenaltyRule4() {
}
{
// Dark cell ratio = 5%
- ByteMatrix matrix = new ByteMatrix(1, 2);
+ ByteMatrix matrix = new ByteMatrix(2, 1);
matrix.set(0, 0, 0);
matrix.set(0, 0, 1);
assertEquals(0, MaskUtil.applyMaskPenaltyRule4(matrix));
}
{
// Dark cell ratio = 66.67%
- ByteMatrix matrix = new ByteMatrix(1, 6);
+ ByteMatrix matrix = new ByteMatrix(6, 1);
matrix.set(0, 0, 0);
- matrix.set(0, 1, 1);
- matrix.set(0, 2, 1);
- matrix.set(0, 3, 1);
- matrix.set(0, 4, 1);
- matrix.set(0, 5, 0);
+ matrix.set(1, 0, 1);
+ matrix.set(2, 0, 1);
+ matrix.set(3, 0, 1);
+ matrix.set(4, 0, 1);
+ matrix.set(5, 0, 0);
assertEquals(30, MaskUtil.applyMaskPenaltyRule4(matrix));
}
}
public void testtoString() {
ByteMatrix array = new ByteMatrix(3, 3);
array.set(0, 0, 0);
- array.set(0, 1, 1);
- array.set(0, 2, 0);
array.set(1, 0, 1);
+ array.set(2, 0, 0);
+ array.set(0, 1, 1);
array.set(1, 1, 0);
- array.set(1, 2, 1);
- array.set(2, 0, -1);
- array.set(2, 1, -1);
+ array.set(2, 1, 1);
+ array.set(0, 2, -1);
+ array.set(1, 2, -1);
array.set(2, 2, -1);
String expected = " 0 1 0\n" + " 1 0 1\n" + " \n";
assertEquals(expected, array.toString());
ByteMatrix matrix = new ByteMatrix(2, 2);
MatrixUtil.clearMatrix(matrix);
assertEquals(-1, matrix.get(0, 0));
- assertEquals(-1, matrix.get(0, 1));
assertEquals(-1, matrix.get(1, 0));
+ assertEquals(-1, matrix.get(0, 1));
assertEquals(-1, matrix.get(1, 1));
}
// Just set bogus zero/one values.
for (int y = 0; y < 45; ++y) {
for (int x = 0; x < 45; ++x) {
- matrix.set(y, x, (y + x) % 2);
+ matrix.set(x, y, (y + x) % 2);
}
}
ByteMatrix matrix = new ByteMatrix(21, 21);
for (int y = 0; y < 21; ++y) {
for (int x = 0; x < 21; ++x) {
- matrix.set(y, x, (y + x) % 2);
+ matrix.set(x, y, (y + x) % 2);
}
}
qrCode.setMatrix(matrix);