001/* 002 * Copyright 2007 ZXing authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.itextpdf.text.pdf.qrcode; 018 019/** 020 * <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common 021 * module, x is the column position, and y is the row position. The ordering is always x, y. 022 * The origin is at the top-left.</p> 023 * 024 * <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins 025 * with a new int. This is done intentionally so that we can copy out a row into a BitArray very 026 * efficiently.</p> 027 * 028 * <p>The ordering of bits is row-major. Within each int, the least significant bits are used first, 029 * meaning they represent lower x values. This is compatible with BitArray's implementation.</p> 030 * 031 * @author Sean Owen 032 * @author dswitkin@google.com (Daniel Switkin) 033 * @since 5.0.2 034 */ 035public final class BitMatrix { 036 037 // TODO: Just like BitArray, these need to be public so ProGuard can inline them. 038 public final int width; 039 public final int height; 040 public final int rowSize; 041 public final int[] bits; 042 043 // A helper to construct a square matrix. 044 public BitMatrix(int dimension) { 045 this(dimension, dimension); 046 } 047 048 public BitMatrix(int width, int height) { 049 if (width < 1 || height < 1) { 050 throw new IllegalArgumentException("Both dimensions must be greater than 0"); 051 } 052 this.width = width; 053 this.height = height; 054 int rowSize = width >> 5; 055 if ((width & 0x1f) != 0) { 056 rowSize++; 057 } 058 this.rowSize = rowSize; 059 bits = new int[rowSize * height]; 060 } 061 062 /** 063 * <p>Gets the requested bit, where true means black.</p> 064 * 065 * @param x The horizontal component (i.e. which column) 066 * @param y The vertical component (i.e. which row) 067 * @return value of given bit in matrix 068 */ 069 public boolean get(int x, int y) { 070 int offset = y * rowSize + (x >> 5); 071 return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; 072 } 073 074 /** 075 * <p>Sets the given bit to true.</p> 076 * 077 * @param x The horizontal component (i.e. which column) 078 * @param y The vertical component (i.e. which row) 079 */ 080 public void set(int x, int y) { 081 int offset = y * rowSize + (x >> 5); 082 bits[offset] |= 1 << (x & 0x1f); 083 } 084 085 /** 086 * <p>Flips the given bit.</p> 087 * 088 * @param x The horizontal component (i.e. which column) 089 * @param y The vertical component (i.e. which row) 090 */ 091 public void flip(int x, int y) { 092 int offset = y * rowSize + (x >> 5); 093 bits[offset] ^= 1 << (x & 0x1f); 094 } 095 096 /** 097 * Clears all bits (sets to false). 098 */ 099 public void clear() { 100 int max = bits.length; 101 for (int i = 0; i < max; i++) { 102 bits[i] = 0; 103 } 104 } 105 106 /** 107 * <p>Sets a square region of the bit matrix to true.</p> 108 * 109 * @param left The horizontal position to begin at (inclusive) 110 * @param top The vertical position to begin at (inclusive) 111 * @param width The width of the region 112 * @param height The height of the region 113 */ 114 public void setRegion(int left, int top, int width, int height) { 115 if (top < 0 || left < 0) { 116 throw new IllegalArgumentException("Left and top must be nonnegative"); 117 } 118 if (height < 1 || width < 1) { 119 throw new IllegalArgumentException("Height and width must be at least 1"); 120 } 121 int right = left + width; 122 int bottom = top + height; 123 if (bottom > this.height || right > this.width) { 124 throw new IllegalArgumentException("The region must fit inside the matrix"); 125 } 126 for (int y = top; y < bottom; y++) { 127 int offset = y * rowSize; 128 for (int x = left; x < right; x++) { 129 bits[offset + (x >> 5)] |= 1 << (x & 0x1f); 130 } 131 } 132 } 133 134 /** 135 * A fast method to retrieve one row of data from the matrix as a BitArray. 136 * 137 * @param y The row to retrieve 138 * @param row An optional caller-allocated BitArray, will be allocated if null or too small 139 * @return The resulting BitArray - this reference should always be used even when passing 140 * your own row 141 */ 142 public BitArray getRow(int y, BitArray row) { 143 if (row == null || row.getSize() < width) { 144 row = new BitArray(width); 145 } 146 int offset = y * rowSize; 147 for (int x = 0; x < rowSize; x++) { 148 row.setBulk(x << 5, bits[offset + x]); 149 } 150 return row; 151 } 152 153 /** 154 * @return The width of the matrix 155 */ 156 public int getWidth() { 157 return width; 158 } 159 160 /** 161 * @return The height of the matrix 162 */ 163 public int getHeight() { 164 return height; 165 } 166 167 /** 168 * This method is for compatibility with older code. It's only logical to call if the matrix 169 * is square, so I'm throwing if that's not the case. 170 * 171 * @return row/column dimension of this matrix 172 */ 173 public int getDimension() { 174 if (width != height) { 175 throw new RuntimeException("Can't call getDimension() on a non-square matrix"); 176 } 177 return width; 178 } 179 180 public String toString() { 181 StringBuffer result = new StringBuffer(height * (width + 1)); 182 for (int y = 0; y < height; y++) { 183 for (int x = 0; x < width; x++) { 184 result.append(get(x, y) ? "X " : " "); 185 } 186 result.append('\n'); 187 } 188 return result.toString(); 189 } 190 191}