diff --git a/src/common/BitMatrix.java b/src/common/BitMatrix.java deleted file mode 100755 index d3f2175..0000000 --- a/src/common/BitMatrix.java +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import java.util.Arrays; - -/** - *

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. - * The origin is at the top-left.

- * - *

Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins - * with a new int. This is done intentionally so that we can copy out a row into a BitArray very - * efficiently.

- * - *

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.

- * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class BitMatrix implements Cloneable { - - private int width; - private int height; - private int rowSize; - private int[] bits; - - /** - * Creates an empty square {@code BitMatrix}. - * - * @param dimension height and width - */ - public BitMatrix(int dimension) { - this(dimension, dimension); - } - - /** - * Creates an empty {@code BitMatrix}. - * - * @param width bit matrix width - * @param height bit matrix height - */ - public BitMatrix(int width, int height) { - if (width < 1 || height < 1) { - throw new IllegalArgumentException("Both dimensions must be greater than 0"); - } - this.width = width; - this.height = height; - this.rowSize = (width + 31) / 32; - bits = new int[rowSize * height]; - } - - private BitMatrix(int width, int height, int rowSize, int[] bits) { - this.width = width; - this.height = height; - this.rowSize = rowSize; - this.bits = bits; - } - - /** - * Interprets a 2D array of booleans as a {@code BitMatrix}, where "true" means an "on" bit. - * - * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows - * @return {@code BitMatrix} representation of image - */ - public static BitMatrix parse(boolean[][] image) { - int height = image.length; - int width = image[0].length; - BitMatrix bits = new BitMatrix(width, height); - for (int i = 0; i < height; i++) { - boolean[] imageI = image[i]; - for (int j = 0; j < width; j++) { - if (imageI[j]) { - bits.set(j, i); - } - } - } - return bits; - } - - public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) { - if (stringRepresentation == null) { - throw new IllegalArgumentException(); - } - - boolean[] bits = new boolean[stringRepresentation.length()]; - int bitsPos = 0; - int rowStartPos = 0; - int rowLength = -1; - int nRows = 0; - int pos = 0; - while (pos < stringRepresentation.length()) { - if (stringRepresentation.charAt(pos) == '\n' || - stringRepresentation.charAt(pos) == '\r') { - if (bitsPos > rowStartPos) { - if (rowLength == -1) { - rowLength = bitsPos - rowStartPos; - } else if (bitsPos - rowStartPos != rowLength) { - throw new IllegalArgumentException("row lengths do not match"); - } - rowStartPos = bitsPos; - nRows++; - } - pos++; - } else if (stringRepresentation.startsWith(setString, pos)) { - pos += setString.length(); - bits[bitsPos] = true; - bitsPos++; - } else if (stringRepresentation.startsWith(unsetString, pos)) { - pos += unsetString.length(); - bits[bitsPos] = false; - bitsPos++; - } else { - throw new IllegalArgumentException( - "illegal character encountered: " + stringRepresentation.substring(pos)); - } - } - - // no EOL at end? - if (bitsPos > rowStartPos) { - if (rowLength == -1) { - rowLength = bitsPos - rowStartPos; - } else if (bitsPos - rowStartPos != rowLength) { - throw new IllegalArgumentException("row lengths do not match"); - } - nRows++; - } - - BitMatrix matrix = new BitMatrix(rowLength, nRows); - for (int i = 0; i < bitsPos; i++) { - if (bits[i]) { - matrix.set(i % rowLength, i / rowLength); - } - } - return matrix; - } - - /** - *

Gets the requested bit, where true means black.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - * @return value of given bit in matrix - */ - public boolean get(int x, int y) { - int offset = y * rowSize + (x / 32); - return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; - } - - /** - *

Sets the given bit to true.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - public void set(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] |= 1 << (x & 0x1f); - } - - public void unset(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] &= ~(1 << (x & 0x1f)); - } - - /** - *

Flips the given bit.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - public void flip(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] ^= 1 << (x & 0x1f); - } - - /** - *

Flips every bit in the matrix.

- */ - public void flip() { - int max = bits.length; - for (int i = 0; i < max; i++) { - bits[i] = ~bits[i]; - } - } - - /** - * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding - * mask bit is set. - * - * @param mask XOR mask - */ - public void xor(BitMatrix mask) { - if (width != mask.width || height != mask.height || rowSize != mask.rowSize) { - throw new IllegalArgumentException("input matrix dimensions do not match"); - } - BitArray rowArray = new BitArray(width); - for (int y = 0; y < height; y++) { - int offset = y * rowSize; - int[] row = mask.getRow(y, rowArray).getBitArray(); - for (int x = 0; x < rowSize; x++) { - bits[offset + x] ^= row[x]; - } - } - } - - /** - * Clears all bits (sets to false). - */ - public void clear() { - int max = bits.length; - for (int i = 0; i < max; i++) { - bits[i] = 0; - } - } - - /** - *

Sets a square region of the bit matrix to true.

- * - * @param left The horizontal position to begin at (inclusive) - * @param top The vertical position to begin at (inclusive) - * @param width The width of the region - * @param height The height of the region - */ - public void setRegion(int left, int top, int width, int height) { - if (top < 0 || left < 0) { - throw new IllegalArgumentException("Left and top must be nonnegative"); - } - if (height < 1 || width < 1) { - throw new IllegalArgumentException("Height and width must be at least 1"); - } - int right = left + width; - int bottom = top + height; - if (bottom > this.height || right > this.width) { - throw new IllegalArgumentException("The region must fit inside the matrix"); - } - for (int y = top; y < bottom; y++) { - int offset = y * rowSize; - for (int x = left; x < right; x++) { - bits[offset + (x / 32)] |= 1 << (x & 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 - */ - public BitArray getRow(int y, BitArray row) { - if (row == null || row.getSize() < width) { - row = new BitArray(width); - } else { - row.clear(); - } - int offset = y * rowSize; - for (int x = 0; x < rowSize; x++) { - row.setBulk(x * 32, bits[offset + x]); - } - return row; - } - - /** - * @param y row to set - * @param row {@link BitArray} to copy from - */ - public void setRow(int y, BitArray row) { - System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize); - } - - /** - * Modifies this {@code BitMatrix} to represent the same but rotated the given degrees (0, 90, 180, 270) - * - * @param degrees number of degrees to rotate through counter-clockwise (0, 90, 180, 270) - */ - public void rotate(int degrees) { - switch (degrees % 360) { - case 0: - return; - case 90: - rotate90(); - return; - case 180: - rotate180(); - return; - case 270: - rotate90(); - rotate180(); - return; - } - throw new IllegalArgumentException("degrees must be a multiple of 0, 90, 180, or 270"); - } - - /** - * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees - */ - public void rotate180() { - BitArray topRow = new BitArray(width); - BitArray bottomRow = new BitArray(width); - int maxHeight = (height + 1) / 2; - for (int i = 0; i < maxHeight; i++) { - topRow = getRow(i, topRow); - int bottomRowIndex = height - 1 - i; - bottomRow = getRow(bottomRowIndex, bottomRow); - topRow.reverse(); - bottomRow.reverse(); - setRow(i, bottomRow); - setRow(bottomRowIndex, topRow); - } - } - - /** - * Modifies this {@code BitMatrix} to represent the same but rotated 90 degrees counterclockwise - */ - public void rotate90() { - int newWidth = height; - int newHeight = width; - int newRowSize = (newWidth + 31) / 32; - int[] newBits = new int[newRowSize * newHeight]; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int offset = y * rowSize + (x / 32); - if (((bits[offset] >>> (x & 0x1f)) & 1) != 0) { - int newOffset = (newHeight - 1 - x) * newRowSize + (y / 32); - newBits[newOffset] |= 1 << (y & 0x1f); - } - } - } - width = newWidth; - height = newHeight; - rowSize = newRowSize; - bits = newBits; - } - - /** - * This is useful in detecting the enclosing rectangle of a 'pure' barcode. - * - * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white - */ - public int[] getEnclosingRectangle() { - int left = width; - int top = height; - int right = -1; - int bottom = -1; - - for (int y = 0; y < height; y++) { - for (int x32 = 0; x32 < rowSize; x32++) { - int theBits = bits[y * rowSize + x32]; - if (theBits != 0) { - if (y < top) { - top = y; - } - if (y > bottom) { - bottom = y; - } - if (x32 * 32 < left) { - int bit = 0; - while ((theBits << (31 - bit)) == 0) { - bit++; - } - if ((x32 * 32 + bit) < left) { - left = x32 * 32 + bit; - } - } - if (x32 * 32 + 31 > right) { - int bit = 31; - while ((theBits >>> bit) == 0) { - bit--; - } - if ((x32 * 32 + bit) > right) { - right = x32 * 32 + bit; - } - } - } - } - } - - if (right < left || bottom < top) { - return null; - } - - return new int[] {left, top, right - left + 1, bottom - top + 1}; - } - - /** - * This is useful in detecting a corner of a 'pure' barcode. - * - * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white - */ - public int[] getTopLeftOnBit() { - int bitsOffset = 0; - while (bitsOffset < bits.length && bits[bitsOffset] == 0) { - bitsOffset++; - } - if (bitsOffset == bits.length) { - return null; - } - int y = bitsOffset / rowSize; - int x = (bitsOffset % rowSize) * 32; - - int theBits = bits[bitsOffset]; - int bit = 0; - while ((theBits << (31 - bit)) == 0) { - bit++; - } - x += bit; - return new int[] {x, y}; - } - - public int[] getBottomRightOnBit() { - int bitsOffset = bits.length - 1; - while (bitsOffset >= 0 && bits[bitsOffset] == 0) { - bitsOffset--; - } - if (bitsOffset < 0) { - return null; - } - - int y = bitsOffset / rowSize; - int x = (bitsOffset % rowSize) * 32; - - int theBits = bits[bitsOffset]; - int bit = 31; - while ((theBits >>> bit) == 0) { - bit--; - } - x += bit; - - return new int[] {x, y}; - } - - /** - * @return The width of the matrix - */ - public int getWidth() { - return width; - } - - /** - * @return The height of the matrix - */ - public int getHeight() { - return height; - } - - /** - * @return The row size of the matrix - */ - public int getRowSize() { - return rowSize; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof BitMatrix)) { - return false; - } - BitMatrix other = (BitMatrix) o; - return width == other.width && height == other.height && rowSize == other.rowSize && - Arrays.equals(bits, other.bits); - } - - @Override - public int hashCode() { - int hash = width; - hash = 31 * hash + width; - hash = 31 * hash + height; - hash = 31 * hash + rowSize; - hash = 31 * hash + Arrays.hashCode(bits); - return hash; - } - - /** - * @return string representation using "X" for set and " " for unset bits - */ - @Override - public String toString() { - return toString("X ", " "); - } - - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @return string representation of entire matrix utilizing given strings - */ - public String toString(String setString, String unsetString) { - return buildToString(setString, unsetString, "\n"); - } - - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @param lineSeparator newline character in string representation - * @return string representation of entire matrix utilizing given strings and line separator - * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always - */ - @Deprecated - public String toString(String setString, String unsetString, String lineSeparator) { - return buildToString(setString, unsetString, lineSeparator); - } - - private String buildToString(String setString, String unsetString, String lineSeparator) { - StringBuilder result = new StringBuilder(height * (width + 1)); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - result.append(get(x, y) ? setString : unsetString); - } - result.append(lineSeparator); - } - return result.toString(); - } - - @Override - public BitMatrix clone() { - return new BitMatrix(width, height, rowSize, bits.clone()); - } - -} diff --git a/src/common/mod.rs b/src/common/mod.rs index 735ae31..bfb9bdb 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -713,3 +713,602 @@ impl DetectorRXingResult { return self.points; } } + +/* + * Copyright 2007 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// package com.google.zxing.common; + +// import java.util.Arrays; + +/** + *

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. + * The origin is at the top-left.

+ * + *

Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins + * with a new int. This is done intentionally so that we can copy out a row into a BitArray very + * efficiently.

+ * + *

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.

+ * + * @author Sean Owen + * @author dswitkin@google.com (Daniel Switkin) + */ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BitMatrix { + width: u32, + height: u32, + rowSize: usize, + bits: Vec, +} + +impl BitMatrix { + /** + * Creates an empty square {@code BitMatrix}. + * + * @param dimension height and width + */ + pub fn with_single_dimension(dimension: u32) -> Self { + Self::new(dimension, dimension) + } + + /** + * Creates an empty {@code BitMatrix}. + * + * @param width bit matrix width + * @param height bit matrix height + */ + pub fn new(width: u32, height: u32) -> Result { + if width < 1 || height < 1 { + return Err(IllegalArgumentException::new( + "Both dimensions must be greater than 0", + )); + } + Ok(Self { + width, + height, + rowSize: (width + 31) / 32, + bits: vec![0; ((width + 31) / 32) * height], + }) + // this.width = width; + // this.height = height; + // this.rowSize = (width + 31) / 32; + // bits = new int[rowSize * height]; + } + + fn with_all_data(&self, width: u32, height: u32, rowSize: usize, bits: Vec) -> Self { + Self { + width, + height, + rowSize, + bits, + } + } + + /** + * Interprets a 2D array of booleans as a {@code BitMatrix}, where "true" means an "on" bit. + * + * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows + * @return {@code BitMatrix} representation of image + */ + pub fn parse(image: &[[bool]]) -> Self { + let height = image.len(); + let width = image[0].len(); + let bits = BitMatrix::new(width, height).unwrap(); + for i in 0..height { + //for (int i = 0; i < height; i++) { + let imageI = image[i]; + for j in 0..width { + //for (int j = 0; j < width; j++) { + if imageI[j] { + bits.set(j, i); + } + } + } + return bits; + } + + pub fn parse( + stringRepresentation: &str, + setString: &str, + unsetString: &str, + ) -> Result { + // cannot pass nulls in rust + // if (stringRepresentation == null) { + // throw new IllegalArgumentException(); + // } + + let bits = Vec::with_capacity(stringRepresentation.length()); + let bitsPos = 0; + let rowStartPos = 0; + let rowLength = -1; + let nRows = 0; + let pos = 0; + while pos < stringRepresentation.length() { + if stringRepresentation.charAt(pos) == '\n' || stringRepresentation.charAt(pos) == '\r' + { + if bitsPos > rowStartPos { + if rowLength == -1 { + rowLength = bitsPos - rowStartPos; + } else if bitsPos - rowStartPos != rowLength { + return Err(IllegalArgumentException::new("row lengths do not match")); + } + rowStartPos = bitsPos; + nRows += 1; + } + pos += 1; + } else if stringRepresentation.startsWith(setString, pos) { + pos += setString.length(); + bits[bitsPos] = true; + bitsPos += 1; + } else if stringRepresentation.startsWith(unsetString, pos) { + pos += unsetString.length(); + bits[bitsPos] = false; + bitsPos += 1; + } else { + return Err(IllegalArgumentException::new(&format!( + "illegal character encountered: {}", + stringRepresentation.substring(pos) + ))); + } + } + + // no EOL at end? + if bitsPos > rowStartPos { + if rowLength == -1 { + rowLength = bitsPos - rowStartPos; + } else if bitsPos - rowStartPos != rowLength { + return Err(IllegalArgumentException::new("row lengths do not match")); + } + nRows += 1; + } + + let matrix = BitMatrix::new(rowLength, nRows); + for i in 0..bitsPos { + //for (int i = 0; i < bitsPos; i++) { + if bits[i] { + matrix.set(i % rowLength, i / rowLength); + } + } + return matrix; + } + + /** + *

Gets the requested bit, where true means black.

+ * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + * @return value of given bit in matrix + */ + pub fn get(&self, x: u32, y: u32) -> bool { + let offset = y * self.rowSize + (x / 32); + return ((self.bits[offset] >> (x & 0x1f)) & 1) != 0; + } + + /** + *

Sets the given bit to true.

+ * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + */ + pub fn set(&self, x: u32, y: u32) { + let offset = y * self.rowSize + (x / 32); + self.bits[offset] |= 1 << (x & 0x1f); + } + + pub fn unset(&self, x: u32, y: u32) { + let offset = y * self.rowSize + (x / 32); + self.bits[offset] &= !(1 << (x & 0x1f)); + } + + /** + *

Flips the given bit.

+ * + * @param x The horizontal component (i.e. which column) + * @param y The vertical component (i.e. which row) + */ + pub fn flip(&self, x: u32, y: u32) { + let offset = y * self.rowSize + (x / 32); + self.bits[offset] ^= 1 << (x & 0x1f); + } + + /** + *

Flips every bit in the matrix.

+ */ + pub fn flip(&self) { + let max = self.bits.len(); + for i in 0..max { + //for (int i = 0; i < max; i++) { + self.bits[i] = !self.bits[i]; + } + } + + /** + * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding + * mask bit is set. + * + * @param mask XOR mask + */ + pub fn xor(&self, mask: &BitMatrix) -> Result<(), IllegalArgumentException> { + if self.width != mask.width || self.height != mask.height || self.rowSize != mask.rowSize { + return Err(IllegalArgumentException::new( + "input matrix dimensions do not match", + )); + } + let rowArray = BitArray::with_size(self.width); + for y in 0..self.height { + //for (int y = 0; y < height; y++) { + let offset = y * self.rowSize; + let row = mask.getRow(y, self.rowArray).getBitArray(); + for x in 0..self.rowSize { + //for (int x = 0; x < rowSize; x++) { + self.bits[offset + x] ^= row[x]; + } + } + Ok(()) + } + + /** + * Clears all bits (sets to false). + */ + pub fn clear(&self) { + let max = self.bits.len(); + for i in 0..max { + //for (int i = 0; i < max; i++) { + self.bits[i] = 0; + } + } + + /** + *

Sets a square region of the bit matrix to true.

+ * + * @param left The horizontal position to begin at (inclusive) + * @param top The vertical position to begin at (inclusive) + * @param width The width of the region + * @param height The height of the region + */ + pub fn setRegion( + &self, + left: u32, + top: u32, + width: u32, + height: u32, + ) -> Result<(), IllegalArgumentException> { + if top < 0 || left < 0 { + return Err(IllegalArgumentException::new( + "Left and top must be nonnegative", + )); + } + if height < 1 || width < 1 { + return Err(IllegalArgumentException::new( + "Height and width must be at least 1", + )); + } + let right = left + width; + let bottom = top + height; + if bottom > self.height || right > self.width { + return Err(IllegalArgumentException::new( + "The region must fit inside the matrix", + )); + } + for y in top..bottom { + //for (int y = top; y < bottom; y++) { + let offset = y * self.rowSize; + for x in left..right { + //for (int x = left; x < right; x++) { + self.bits[offset + (x / 32)] |= 1 << (x & 0x1f); + } + } + Ok(()) + } + + /** + * 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 + */ + pub fn getRow(&self, y: u32, row: &BitArray) -> BitArray { + let rw: BitArray = if row.getSize() < self.width { + row = &BitArray::with_size(self.width) + } else { + row.clear(); + row + }; + + let offset = y * self.rowSize; + for x in 0..self.rowSize { + //for (int x = 0; x < rowSize; x++) { + rw.setBulk(x * 32, self.bits[offset + x]); + } + return rw; + } + + /** + * @param y row to set + * @param row {@link BitArray} to copy from + */ + pub fn setRow(&self, y: u32, row: &BitArray) { + return self.bits[y * self.rowSize..self.rowSize] + .clone_from_slice(&row.getBitArray()[0..self.rowSize]); + //System.arraycopy(row.getBitArray(), 0, self.bits, y * self.rowSize, self.rowSize); + } + + /** + * Modifies this {@code BitMatrix} to represent the same but rotated the given degrees (0, 90, 180, 270) + * + * @param degrees number of degrees to rotate through counter-clockwise (0, 90, 180, 270) + */ + pub fn rotate(&self, degrees: u32) -> Result<(), IllegalArgumentException> { + match degrees % 360 { + 0 => Ok(()), + 90 => { + self.rotate90(); + Ok(()) + } + 180 => { + self.rotate180(); + Ok(()) + } + 270 => { + self.rotate90(); + self.rotate180(); + Ok(()) + } + _ => Err(IllegalArgumentException::new( + "degrees must be a multiple of 0, 90, 180, or 270", + )), + } + } + + /** + * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees + */ + pub fn rotate180(&self) { + let mut topRow = BitArray::with_size(self.width); + let mut bottomRow = BitArray::with_size(self.width); + let mut maxHeight = (self.height + 1) / 2; + for i in 0..maxHeight { + //for (int i = 0; i < maxHeight; i++) { + topRow = self.getRow(i, &topRow); + let bottomRowIndex = self.height - 1 - i; + bottomRow = self.getRow(bottomRowIndex, &bottomRow); + topRow.reverse(); + bottomRow.reverse(); + self.setRow(i, &bottomRow); + self.setRow(bottomRowIndex, &topRow); + } + } + + /** + * Modifies this {@code BitMatrix} to represent the same but rotated 90 degrees counterclockwise + */ + pub fn rotate90(&self) { + let mut newWidth = self.height; + let mut newHeight = self.width; + let mut newRowSize = (newWidth + 31) / 32; + let mut newBits = Vec::with_capacity(newRowSize * newHeight); + + for y in 0..self.height { + //for (int y = 0; y < height; y++) { + for x in 0..self.width { + //for (int x = 0; x < width; x++) { + let offset = y * self.rowSize + (x / 32); + if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 { + let newOffset = (newHeight - 1 - x) * newRowSize + (y / 32); + newBits[newOffset] |= 1 << (y & 0x1f); + } + } + } + self.width = newWidth; + self.height = newHeight; + self.rowSize = newRowSize; + self.bits = newBits; + } + + /** + * This is useful in detecting the enclosing rectangle of a 'pure' barcode. + * + * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white + */ + pub fn getEnclosingRectangle(&self) -> Option> { + let left = self.width; + let top = self.height; + let right = -1; + let bottom = -1; + + for y in 0..self.height { + //for (int y = 0; y < height; y++) { + for x32 in 0..self.rowSize { + //for (int x32 = 0; x32 < rowSize; x32++) { + let theBits = self.bits[y * self.rowSize + x32]; + if theBits != 0 { + if y < top { + top = y; + } + if y > bottom { + bottom = y; + } + if x32 * 32 < left { + let bit = 0; + while (theBits << (31 - bit)) == 0 { + bit += 1; + } + if (x32 * 32 + bit) < left { + left = x32 * 32 + bit; + } + } + if x32 * 32 + 31 > right { + let bit = 31; + while (theBits >> bit) == 0 { + bit -= 1; + } + if (x32 * 32 + bit) > right { + right = x32 * 32 + bit; + } + } + } + } + } + + if right < left || bottom < top { + return None; + } + + return Some(vec![left, top, right - left + 1, bottom - top + 1]); + } + + /** + * This is useful in detecting a corner of a 'pure' barcode. + * + * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white + */ + pub fn getTopLeftOnBit(&self) -> Option> { + let bitsOffset = 0; + while bitsOffset < self.bits.length && self.bits[bitsOffset] == 0 { + bitsOffset += 1; + } + if bitsOffset == self.bits.length { + return None; + } + let y = bitsOffset / self.rowSize; + let x = (bitsOffset % self.rowSize) * 32; + + let theBits = self.bits[bitsOffset]; + let bit = 0; + while (theBits << (31 - bit)) == 0 { + bit += 1; + } + x += bit; + return Some(vec![x, y]); + } + + pub fn getBottomRightOnBit(&self) -> Option> { + let bitsOffset = self.bits.length - 1; + while bitsOffset >= 0 && self.bits[bitsOffset] == 0 { + bitsOffset -= 1; + } + if bitsOffset < 0 { + return None; + } + + let y = bitsOffset / self.rowSize; + let x = (bitsOffset % self.rowSize) * 32; + + let theBits = self.bits[bitsOffset]; + let bit = 31; + while (theBits >> bit) == 0 { + bit -= 1; + } + x += bit; + + return Some(vec![x, y]); + } + + /** + * @return The width of the matrix + */ + pub fn getWidth(&self) -> u32 { + return self.width; + } + + /** + * @return The height of the matrix + */ + pub fn getHeight(&self) -> u32 { + return self.height; + } + + /** + * @return The row size of the matrix + */ + pub fn getRowSize(&self) -> usize { + return self.rowSize; + } + + // @Override + // public boolean equals(Object o) { + // if (!(o instanceof BitMatrix)) { + // return false; + // } + // BitMatrix other = (BitMatrix) o; + // return width == other.width && height == other.height && rowSize == other.rowSize && + // Arrays.equals(bits, other.bits); + // } + + // @Override + // public int hashCode() { + // int hash = width; + // hash = 31 * hash + width; + // hash = 31 * hash + height; + // hash = 31 * hash + rowSize; + // hash = 31 * hash + Arrays.hashCode(bits); + // return hash; + // } + + /** + * @param setString representation of a set bit + * @param unsetString representation of an unset bit + * @return string representation of entire matrix utilizing given strings + */ + pub fn toString(&self, setString: &str, unsetString: &str) -> String { + return self.buildToString(setString, unsetString, "\n"); + } + + /** + * @param setString representation of a set bit + * @param unsetString representation of an unset bit + * @param lineSeparator newline character in string representation + * @return string representation of entire matrix utilizing given strings and line separator + * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always + */ + // @Deprecated + // public String toString(String setString, String unsetString, String lineSeparator) { + // return buildToString(setString, unsetString, lineSeparator); + // } + + fn buildToString(&self, setString: &str, unsetString: &str, lineSeparator: &str) -> String { + let result = String::with_capacity(self.height * (self.width + 1)); + for y in 0..self.height { + //for (int y = 0; y < height; y++) { + for x in 0..self.width { + //for (int x = 0; x < width; x++) { + result.push_str(if self.get(x, y) { + setString + } else { + unsetString + }); + } + result.push_str(lineSeparator); + } + return result; + } + + // @Override + // public BitMatrix clone() { + // return new BitMatrix(width, height, rowSize, bits.clone()); + // } +} + +impl fmt::Display for BitMatrix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.toString("X ", " ")) + } +}