diff --git a/src/common/bit_array.rs b/src/common/bit_array.rs
new file mode 100644
index 0000000..42ac843
--- /dev/null
+++ b/src/common/bit_array.rs
@@ -0,0 +1,405 @@
+
+/*
+ * 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;
+
+use std::{fmt, cmp};
+
+use crate::Exceptions;
+
+static EMPTY_BITS: [u32; 0] = [0; 0];
+static LOAD_FACTOR: f32 = 0.75f32;
+
+/**
+ *
A simple, fast array of bits, represented compactly by an array of ints internally.
+ *
+ * @author Sean Owen
+ */
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub struct BitArray {
+ bits: Vec,
+ size: usize,
+}
+
+impl BitArray {
+ pub fn new() -> Self {
+ Self {
+ bits: EMPTY_BITS.to_vec(),
+ size: 0,
+ }
+ }
+
+ pub fn with_size(size: usize) -> Self {
+ Self {
+ bits: BitArray::makeArray(size),
+ size: size,
+ }
+ }
+
+ // For testing only
+ pub fn with_initial_values(bits: Vec, size: usize) -> Self {
+ Self {
+ bits: bits,
+ size: size,
+ }
+ }
+
+ pub fn getSize(&self) -> usize {
+ self.size
+ }
+
+ pub fn getSizeInBytes(&self) -> usize {
+ return (self.size + 7) / 8;
+ }
+
+ fn ensure_capacity(&mut self, newSize: usize) {
+ if newSize > self.bits.len() * 32 {
+ let mut newBits = BitArray::makeArray((newSize as f32 / LOAD_FACTOR).ceil() as usize);
+ //System.arraycopy(bits, 0, newBits, 0, bits.length);
+ newBits[0..self.bits.len()].clone_from_slice(&self.bits[0..self.bits.len()]);
+ self.bits = newBits;
+ }
+ }
+
+ /**
+ * @param i bit to get
+ * @return true iff bit i is set
+ */
+ pub fn get(&self, i: usize) -> bool {
+ return (self.bits[i / 32] & (1 << (i & 0x1F))) != 0;
+ }
+
+ /**
+ * Sets bit i.
+ *
+ * @param i bit to set
+ */
+ pub fn set(&mut self, i: usize) {
+ self.bits[i / 32] |= 1 << (i & 0x1F);
+ }
+
+ /**
+ * Flips bit i.
+ *
+ * @param i bit to set
+ */
+ pub fn flip(&mut self, i: usize) {
+ self.bits[i / 32] ^= 1 << (i & 0x1F);
+ }
+
+ /**
+ * @param from first bit to check
+ * @return index of first bit that is set, starting from the given index, or size if none are set
+ * at or beyond this given index
+ * @see #getNextUnset(int)
+ */
+ pub fn getNextSet(&self, from: usize) -> usize {
+ if from >= self.size {
+ return self.size;
+ }
+ let mut bitsOffset = from / 32;
+ let mut currentBits = self.bits[bitsOffset] as i64;
+ // mask off lesser bits first
+ currentBits &= -(1 << (from & 0x1F));
+ while currentBits == 0 {
+ bitsOffset += 1;
+ if bitsOffset == self.bits.len() {
+ return self.size;
+ }
+ currentBits = self.bits[bitsOffset] as i64;
+ }
+ let result = (bitsOffset * 32) + currentBits.trailing_zeros() as usize;
+ cmp::min(result, self.size)
+ }
+
+ /**
+ * @param from index to start looking for unset bit
+ * @return index of next unset bit, or {@code size} if none are unset until the end
+ * @see #getNextSet(int)
+ */
+ pub fn getNextUnset(&self, from: usize) -> usize {
+ if from >= self.size {
+ return self.size;
+ }
+ let mut bitsOffset = from / 32;
+ let mut currentBits = !self.bits[bitsOffset] as i32;
+ // mask off lesser bits first
+ currentBits &= -(1 << (from & 0x1F));
+ while currentBits == 0 {
+ bitsOffset += 1;
+ if bitsOffset == self.bits.len() {
+ return self.size;
+ }
+ currentBits = !self.bits[bitsOffset] as i32;
+ }
+ let result = (bitsOffset * 32) + currentBits.trailing_zeros() as usize;
+ return cmp::min(result, self.size);
+ }
+
+ /**
+ * Sets a block of 32 bits, starting at bit i.
+ *
+ * @param i first bit to set
+ * @param newBits the new value of the next 32 bits. Note again that the least-significant bit
+ * corresponds to bit i, the next-least-significant to i+1, and so on.
+ */
+ pub fn setBulk(&mut self, i: usize, newBits: u32) {
+ self.bits[i / 32] = newBits;
+ }
+
+ /**
+ * Sets a range of bits.
+ *
+ * @param start start of range, inclusive.
+ * @param end end of range, exclusive
+ */
+ pub fn setRange(&mut self, start: usize, end: usize) -> Result<(), Exceptions> {
+ let mut end = end;
+ if end < start || end > self.size {
+ return Err(Exceptions::IllegalArgumentException(
+ "end < start || start < 0 || end > self.size".to_owned(),
+ ));
+ }
+ if end == start {
+ return Ok(());
+ }
+ end -= 1; // will be easier to treat this as the last actually set bit -- inclusive
+ let firstInt = start / 32;
+ let lastInt = end / 32;
+ for i in firstInt..=lastInt {
+ //for (int i = firstInt; i <= lastInt; i++) {
+ let firstBit = if i > firstInt { 0 } else { start & 0x1F };
+ let lastBit = if i < lastInt { 31 } else { end & 0x1F };
+ // Ones from firstBit to lastBit, inclusive
+ let mask: u64 = (2 << lastBit) - (1 << firstBit);
+ self.bits[i] |= mask as u32;
+ }
+ Ok(())
+ }
+
+ /**
+ * Clears all bits (sets to false).
+ */
+ pub fn clear(&mut self) {
+ let max = self.bits.len();
+ for i in 0..max {
+ //for (int i = 0; i < max; i++) {
+ self.bits[i] = 0;
+ }
+ }
+
+ /**
+ * Efficient method to check if a range of bits is set, or not set.
+ *
+ * @param start start of range, inclusive.
+ * @param end end of range, exclusive
+ * @param value if true, checks that bits in range are set, otherwise checks that they are not set
+ * @return true iff all bits are set or not set in range, according to value argument
+ * @throws IllegalArgumentException if end is less than start or the range is not contained in the array
+ */
+ pub fn isRange(&self, start: usize, end: usize, value: bool) -> Result {
+ let mut end = end;
+ if end < start || end > self.size {
+ return Err(Exceptions::IllegalArgumentException(
+ "end < start || start < 0 || end > self.size".to_owned(),
+ ));
+ }
+ if end == start {
+ return Ok(true); // empty range matches
+ }
+ end -= 1; // will be easier to treat this as the last actually set bit -- inclusive
+ let firstInt = start / 32;
+ let lastInt = end / 32;
+ for i in firstInt..=lastInt {
+ //for (int i = firstInt; i <= lastInt; i++) {
+ let firstBit = if i > firstInt { 0 } else { start & 0x1F };
+ let lastBit = if i < lastInt { 31 } else { end & 0x1F };
+ // Ones from firstBit to lastBit, inclusive
+ let mask: u64 = (2 << lastBit) - (1 << firstBit);
+
+ // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
+ // equals the mask, or we're looking for 0s and the masked portion is not all 0s
+ if (self.bits[i] & mask as u32) != (if value { mask as u32 } else { 0 }) {
+ return Ok(false);
+ }
+ }
+ return Ok(true);
+ }
+
+ pub fn appendBit(&mut self, bit: bool) {
+ self.ensure_capacity(self.size + 1);
+ if bit {
+ self.bits[self.size / 32] |= 1 << (self.size & 0x1F);
+ }
+ self.size += 1;
+ }
+
+ /**
+ * 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.
+ *
+ * @param value {@code int} containing bits to append
+ * @param numBits bits from value to append
+ */
+ pub fn appendBits(&mut self, value: u32, num_bits: usize) -> Result<(), Exceptions> {
+ if num_bits > 32 {
+ return Err(Exceptions::IllegalArgumentException(
+ "Num bits must be between 0 and 32".to_owned(),
+ ));
+ }
+
+ if num_bits == 0 {
+ return Ok(());
+ }
+
+ let mut next_size = self.size;
+ self.ensure_capacity(next_size + num_bits);
+ for numBitsLeft in (0..num_bits).rev() {
+ //for (int numBitsLeft = numBits - 1; numBitsLeft >= 0; numBitsLeft--) {
+ if (value & (1 << numBitsLeft)) != 0 {
+ self.bits[next_size / 32] |= 1 << (next_size & 0x1F);
+ }
+ next_size += 1;
+ }
+ self.size = next_size;
+ Ok(())
+ }
+
+ pub fn appendBitArray(&mut self, other: BitArray) {
+ let otherSize = other.size;
+ self.ensure_capacity(self.size + otherSize);
+ for i in 0..otherSize {
+ //for (int i = 0; i < otherSize; i++) {
+ self.appendBit(other.get(i));
+ }
+ }
+
+ pub fn xor(&mut self, other: &BitArray) -> Result<(), Exceptions> {
+ if self.size != other.size {
+ return Err(Exceptions::IllegalArgumentException(
+ "Sizes don't match".to_owned(),
+ ));
+ }
+ for i in 0..self.bits.len() {
+ //for (int i = 0; i < bits.length; i++) {
+ // The last int could be incomplete (i.e. not have 32 bits in
+ // it) but there is no problem since 0 XOR 0 == 0.
+ self.bits[i] ^= other.bits[i];
+ }
+ Ok(())
+ }
+
+ /**
+ *
+ * @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
+ */
+ pub fn toBytes(&self, bitOffset: usize, array: &mut [u8], offset: usize, numBytes: usize) {
+ let mut bitOffset = bitOffset;
+ for i in 0..numBytes {
+ //for (int i = 0; i < numBytes; i++) {
+ let mut theByte = 0;
+ for j in 0..8 {
+ //for (int j = 0; j < 8; j++) {
+ if self.get(bitOffset) {
+ theByte |= 1 << (7 - j);
+ }
+ bitOffset += 1;
+ }
+ array[offset + i] = theByte;
+ }
+ }
+
+ /**
+ * @return underlying array of ints. The first element holds the first 32 bits, and the least
+ * significant bit is bit 0.
+ */
+ pub fn getBitArray(&self) -> &Vec {
+ return &self.bits;
+ }
+
+ /**
+ * Reverses all bits in the array.
+ */
+ pub fn reverse(&mut self) {
+ let mut newBits = vec![0; self.bits.len()];
+ // reverse all int's first
+ let len = (self.size - 1) / 32;
+ let oldBitsLen = len + 1;
+ for i in 0..oldBitsLen {
+ //for (int i = 0; i < oldBitsLen; i++) {
+ newBits[len - i] = self.bits[i].reverse_bits();
+ }
+ // now correct the int's if the bit size isn't a multiple of 32
+ if self.size != oldBitsLen * 32 {
+ let leftOffset = oldBitsLen * 32 - self.size;
+ let mut currentInt = newBits[0] >> leftOffset;
+ for i in 1..oldBitsLen {
+ //for (int i = 1; i < oldBitsLen; i++) {
+ let nextInt = newBits[i];
+ currentInt |= nextInt << (32 - leftOffset);
+ newBits[i - 1] = currentInt;
+ currentInt = nextInt >> leftOffset;
+ }
+ newBits[oldBitsLen - 1] = currentInt;
+ }
+ self.bits = newBits;
+ }
+
+ fn makeArray(size: usize) -> Vec {
+ return vec![0; (size + 31) / 32];
+ }
+
+ // @Override
+ // public boolean equals(Object o) {
+ // if (!(o instanceof BitArray)) {
+ // return false;
+ // }
+ // BitArray other = (BitArray) o;
+ // return size == other.size && Arrays.equals(bits, other.bits);
+ // }
+
+ // @Override
+ // public int hashCode() {
+ // return 31 * size + Arrays.hashCode(bits);
+ // }
+
+ // @Override
+ // public BitArray clone() {
+ // return new BitArray(bits.clone(), size);
+ // }
+}
+
+impl fmt::Display for BitArray {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut _str = String::with_capacity(self.size + (self.size / 8) + 1);
+ for i in 0..self.size {
+ //for (int i = 0; i < size; i++) {
+ if (i & 0x07) == 0 {
+ _str.push_str(" ");
+ }
+ _str.push_str(if self.get(i) { "X" } else { "." });
+ }
+ write!(f, "{}", _str)
+ }
+}
\ No newline at end of file
diff --git a/src/common/bit_matrix.rs b/src/common/bit_matrix.rs
new file mode 100644
index 0000000..b7a0520
--- /dev/null
+++ b/src/common/bit_matrix.rs
@@ -0,0 +1,628 @@
+
+/*
+ * 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;
+
+use std::fmt;
+
+use crate::Exceptions;
+
+use super::BitArray;
+
+/**
+ * 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, PartialEq, Eq, Hash)]
+pub struct BitMatrix {
+ width: u32,
+ height: u32,
+ row_size: 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).unwrap()
+ }
+
+ /**
+ * 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(Exceptions::IllegalArgumentException(
+ "Both dimensions must be greater than 0".to_owned(),
+ ));
+ }
+ Ok(Self {
+ width,
+ height,
+ row_size: ((width + 31) / 32) as usize,
+ bits: vec![0; (((width + 31) / 32) * height) as usize],
+ })
+ // 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,
+ row_size: 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_bools(image: &Vec>) -> Self {
+ let height: u32 = image.len().try_into().unwrap();
+ let width: u32 = image[0].len().try_into().unwrap();
+ let mut bits = BitMatrix::new(width, height).unwrap();
+ for i in 0..height as usize {
+ //for (int i = 0; i < height; i++) {
+ let imageI = &image[i];
+ for j in 0..width as usize {
+ //for (int j = 0; j < width; j++) {
+ if imageI[j] {
+ bits.set(j as u32, i as u32);
+ }
+ }
+ }
+ return bits;
+ }
+
+ pub fn parse_strings(
+ string_representation: &str,
+ set_string: &str,
+ unset_string: &str,
+ ) -> Result {
+ // cannot pass nulls in rust
+ // if (stringRepresentation == null) {
+ // throw new IllegalArgumentException();
+ // }
+
+ let mut bits = vec![false; string_representation.len()];
+ let mut bitsPos = 0;
+ let mut rowStartPos = 0;
+ let mut rowLength = 0; //-1;
+ let mut first_run = true;
+ let mut nRows = 0;
+ let mut pos = 0;
+ while pos < string_representation.len() {
+ if string_representation.chars().nth(pos).unwrap() == '\n'
+ || string_representation.chars().nth(pos).unwrap() == '\r'
+ {
+ if bitsPos > rowStartPos {
+ //if rowLength == -1 {
+ if first_run {
+ first_run = false;
+ rowLength = bitsPos - rowStartPos;
+ } else if bitsPos - rowStartPos != rowLength {
+ return Err(Exceptions::IllegalArgumentException(
+ "row lengths do not match".to_owned(),
+ ));
+ }
+ rowStartPos = bitsPos;
+ nRows += 1;
+ }
+ pos += 1;
+ } else if string_representation[pos..].starts_with(set_string) {
+ pos += set_string.len();
+ bits[bitsPos] = true;
+ bitsPos += 1;
+ } else if string_representation[pos..].starts_with(unset_string) {
+ pos += unset_string.len();
+ bits[bitsPos] = false;
+ bitsPos += 1;
+ } else {
+ return Err(Exceptions::IllegalArgumentException(format!(
+ "illegal character encountered: {}",
+ string_representation[pos..].to_owned()
+ )));
+ }
+ }
+
+ // no EOL at end?
+ if bitsPos > rowStartPos {
+ //if rowLength == -1 {
+ if first_run {
+ first_run = false;
+ rowLength = bitsPos - rowStartPos;
+ } else if bitsPos - rowStartPos != rowLength {
+ return Err(Exceptions::IllegalArgumentException(
+ "row lengths do not match".to_owned(),
+ ));
+ }
+ nRows += 1;
+ }
+
+ let mut matrix = BitMatrix::new(rowLength.try_into().unwrap(), nRows)?;
+ for i in 0..bitsPos {
+ //for (int i = 0; i < bitsPos; i++) {
+ if bits[i] {
+ matrix.set(
+ (i % rowLength).try_into().unwrap(),
+ (i / rowLength).try_into().unwrap(),
+ );
+ }
+ }
+ return Ok(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 as usize * self.row_size + (x as usize / 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(&mut self, x: u32, y: u32) {
+ let offset = y as usize * self.row_size + (x as usize / 32);
+ self.bits[offset] |= 1 << (x & 0x1f);
+ }
+
+ pub fn unset(&mut self, x: u32, y: u32) {
+ let offset = y as usize * self.row_size + (x as usize / 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_coords(&mut self, x: u32, y: u32) {
+ let offset = y as usize * self.row_size + (x as usize / 32);
+ self.bits[offset] ^= 1 << (x & 0x1f);
+ }
+
+ /**
+ * Flips every bit in the matrix.
+ */
+ pub fn flip_self(&mut 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(&mut self, mask: &BitMatrix) -> Result<(), Exceptions> {
+ if self.width != mask.width || self.height != mask.height || self.row_size != mask.row_size
+ {
+ return Err(Exceptions::IllegalArgumentException(
+ "input matrix dimensions do not match".to_owned(),
+ ));
+ }
+ let rowArray = BitArray::with_size(self.width as usize);
+ for y in 0..self.height {
+ //for (int y = 0; y < height; y++) {
+ let offset = y as usize * self.row_size;
+ let tmp = mask.getRow(y, &rowArray);
+ let row = tmp.getBitArray();
+ for x in 0..self.row_size {
+ //for (int x = 0; x < rowSize; x++) {
+ self.bits[offset + x] ^= row[x];
+ }
+ }
+ Ok(())
+ }
+
+ /**
+ * Clears all bits (sets to false).
+ */
+ pub fn clear(&mut 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(
+ &mut self,
+ left: u32,
+ top: u32,
+ width: u32,
+ height: u32,
+ ) -> Result<(), Exceptions> {
+ // if top < 0 || left < 0 {
+ // return Err(Exceptions::IllegalArgumentException(
+ // "Left and top must be nonnegative".to_owned(),
+ // ));
+ // }
+ if height < 1 || width < 1 {
+ return Err(Exceptions::IllegalArgumentException(
+ "Height and width must be at least 1".to_owned(),
+ ));
+ }
+ let right = left + width;
+ let bottom = top + height;
+ if bottom > self.height || right > self.width {
+ return Err(Exceptions::IllegalArgumentException(
+ "The region must fit inside the matrix".to_owned(),
+ ));
+ }
+ for y in top..bottom {
+ //for (int y = top; y < bottom; y++) {
+ let offset = y as usize * self.row_size;
+ for x in left..right {
+ //for (int x = left; x < right; x++) {
+ self.bits[offset + (x as usize / 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 mut rw: BitArray = if row.getSize() < self.width as usize {
+ BitArray::with_size(self.width as usize)
+ } else {
+ let mut z = row.clone();
+ z.clear();
+ z
+ // row.clear();
+ // row.clone()
+ };
+
+ let offset = y as usize * self.row_size;
+ for x in 0..self.row_size {
+ //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(&mut self, y: u32, row: &BitArray) {
+ return self.bits[y as usize * self.row_size..y as usize * self.row_size + self.row_size]
+ .clone_from_slice(&row.getBitArray()[0..self.row_size]);
+ //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(&mut self, degrees: u32) -> Result<(), Exceptions> {
+ match degrees % 360 {
+ 0 => Ok(()),
+ 90 => {
+ self.rotate90();
+ Ok(())
+ }
+ 180 => {
+ self.rotate180();
+ Ok(())
+ }
+ 270 => {
+ self.rotate90();
+ self.rotate180();
+ Ok(())
+ }
+ _ => Err(Exceptions::IllegalArgumentException(
+ "degrees must be a multiple of 0, 90, 180, or 270".to_owned(),
+ )),
+ }
+ }
+
+ /**
+ * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
+ */
+ pub fn rotate180(&mut self) {
+ let mut topRow = BitArray::with_size(self.width as usize);
+ let mut bottomRow = BitArray::with_size(self.width as usize);
+ let 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(&mut self) {
+ let newWidth = self.height;
+ let newHeight = self.width;
+ let newRowSize = (newWidth + 31) / 32;
+ let mut newBits = vec![0; (newRowSize * newHeight).try_into().unwrap()];
+
+ 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 as usize * self.row_size + (x as usize / 32);
+ if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 {
+ let newOffset: usize = ((newHeight - 1 - x) * newRowSize + (y / 32))
+ .try_into()
+ .unwrap();
+ newBits[newOffset] |= 1 << (y & 0x1f);
+ }
+ }
+ }
+ self.width = newWidth;
+ self.height = newHeight;
+ self.row_size = newRowSize.try_into().unwrap();
+ 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 mut left = self.width;
+ let mut top = self.height;
+ // let right = -1;
+ // let bottom = -1;
+ let mut right: u32 = 0;
+ let mut bottom = 0;
+
+ for y in 0..self.height {
+ //for (int y = 0; y < height; y++) {
+ for x32 in 0..self.row_size {
+ //for (int x32 = 0; x32 < rowSize; x32++) {
+ let theBits = self.bits[y as usize * self.row_size + x32];
+ if theBits != 0 {
+ if y < top {
+ top = y;
+ }
+ if y > bottom {
+ bottom = y;
+ }
+ if x32 * 32 < left.try_into().unwrap() {
+ let mut bit = 0;
+ while (theBits << (31 - bit)) == 0 {
+ bit += 1;
+ }
+ if (x32 * 32 + bit) < left.try_into().unwrap() {
+ left = (x32 * 32 + bit).try_into().unwrap();
+ }
+ }
+ if x32 * 32 + 31 > right.try_into().unwrap() {
+ let mut bit = 31;
+ while (theBits >> bit) == 0 {
+ bit -= 1;
+ }
+ if (x32 * 32 + bit) > right.try_into().unwrap() {
+ right = (x32 * 32 + bit).try_into().unwrap();
+ }
+ }
+ }
+ }
+ }
+
+ 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 mut bitsOffset = 0;
+ while bitsOffset < self.bits.len() && self.bits[bitsOffset] == 0 {
+ bitsOffset += 1;
+ }
+ if bitsOffset == self.bits.len() {
+ return None;
+ }
+ let y = bitsOffset / self.row_size;
+ let mut x = (bitsOffset % self.row_size) * 32;
+
+ let theBits = self.bits[bitsOffset];
+ let mut bit = 0;
+ while (theBits << (31 - bit)) == 0 {
+ bit += 1;
+ }
+ x += bit;
+ return Some(vec![x as u32, y as u32]);
+ }
+
+ pub fn getBottomRightOnBit(&self) -> Option> {
+ let mut bitsOffset = self.bits.len() as i64 - 1;
+ while bitsOffset >= 0 && self.bits[bitsOffset as usize] == 0 {
+ bitsOffset -= 1;
+ }
+ if bitsOffset < 0 {
+ return None;
+ }
+
+ let y = bitsOffset as usize / self.row_size;
+ let mut x = (bitsOffset as usize % self.row_size) * 32;
+
+ let theBits = self.bits[bitsOffset as usize];
+ let mut bit = 31;
+ while (theBits >> bit) == 0 {
+ bit -= 1;
+ }
+ x += bit;
+
+ return Some(vec![x as u32, y as u32]);
+ }
+
+ /**
+ * @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.row_size;
+ }
+
+ // @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 mut result =
+ String::with_capacity((self.height * (self.width + 1)).try_into().unwrap());
+ 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 ", " "))
+ }
+}
\ No newline at end of file
diff --git a/src/common/bit_source.rs b/src/common/bit_source.rs
new file mode 100644
index 0000000..9e0cbba
--- /dev/null
+++ b/src/common/bit_source.rs
@@ -0,0 +1,125 @@
+
+/*
+ * 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;
+
+use std::cmp;
+
+use crate::Exceptions;
+
+/**
+ * This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
+ * number of bits read is not often a multiple of 8.
+ *
+ * This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
+ * it passed in, in which case all bets are off.
+ *
+ * @author Sean Owen
+ */
+pub struct BitSource {
+ bytes: Vec,
+ byte_offset: usize,
+ bit_offset: usize,
+}
+
+impl BitSource {
+ /**
+ * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
+ * Bits are read within a byte from most-significant to least-significant bit.
+ */
+ pub fn new(bytes: Vec) -> Self {
+ Self {
+ bytes,
+ byte_offset: 0,
+ bit_offset: 0,
+ }
+ }
+
+ /**
+ * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
+ */
+ pub fn getBitOffset(&self) -> usize {
+ return self.bit_offset;
+ }
+
+ /**
+ * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
+ */
+ pub fn getByteOffset(&self) -> usize {
+ return self.byte_offset;
+ }
+
+ /**
+ * @param numBits number of bits to read
+ * @return int representing the bits read. The bits will appear as the least-significant
+ * bits of the int
+ * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available
+ */
+ pub fn readBits(&mut self, numBits: usize) -> Result {
+ if numBits < 1 || numBits > 32 || numBits > self.available() {
+ return Err(Exceptions::IllegalArgumentException(numBits.to_string()));
+ }
+
+ let mut result: u32 = 0;
+
+ let mut num_bits = numBits;
+
+ // First, read remainder from current byte
+ if self.bit_offset > 0 {
+ let bitsLeft = 8 - self.bit_offset;
+ let toRead = cmp::min(num_bits, bitsLeft);
+ let bitsToNotRead = bitsLeft - toRead;
+ let mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
+
+ result = (self.bytes[self.byte_offset] & mask) as u32 >> bitsToNotRead;
+ num_bits -= toRead;
+ self.bit_offset += toRead;
+ if self.bit_offset == 8 {
+ self.bit_offset = 0;
+ self.byte_offset += 1;
+ }
+ }
+
+ // Next read whole bytes
+ if num_bits > 0 {
+ while num_bits >= 8 {
+ result = (result << 8) | (self.bytes[self.byte_offset] & 0xFF) as u32;
+ // result = ((result as u16) << 8) as u8 | (self.bytes[self.byte_offset]);
+ self.byte_offset += 1;
+ num_bits -= 8;
+ }
+
+ // Finally read a partial byte
+ if num_bits > 0 {
+ let bits_to_not_read = 8 - num_bits;
+ let mask = (0xFF >> bits_to_not_read) << bits_to_not_read;
+ result = (result << num_bits)
+ | ((self.bytes[self.byte_offset] & mask) as u32 >> bits_to_not_read);
+ self.bit_offset += num_bits;
+ }
+ }
+
+ return Ok(result);
+ }
+
+ /**
+ * @return number of bits that can be read successfully
+ */
+ pub fn available(&self) -> usize {
+ return 8 * (self.bytes.len() - self.byte_offset) - self.bit_offset;
+ }
+}
\ No newline at end of file
diff --git a/src/common/bit_source_builder.rs b/src/common/bit_source_builder.rs
new file mode 100644
index 0000000..fd6f34c
--- /dev/null
+++ b/src/common/bit_source_builder.rs
@@ -0,0 +1,68 @@
+
+/*
+ * 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;
+
+// import java.io.ByteArrayOutputStream;
+
+/**
+ * Class that lets one easily build an array of bytes by appending bits at a time.
+ *
+ * @author Sean Owen
+ */
+pub struct BitSourceBuilder {
+ output: Vec,
+ nextByte: u32,
+ bitsLeftInNextByte: u32,
+}
+
+impl BitSourceBuilder {
+ pub fn new() -> Self {
+ Self {
+ output: Vec::new(),
+ nextByte: 0,
+ bitsLeftInNextByte: 8,
+ }
+ }
+
+ pub fn write(&mut self, value: u32, numBits: u32) {
+ if numBits <= self.bitsLeftInNextByte {
+ self.nextByte <<= numBits;
+ self.nextByte |= value;
+ self.bitsLeftInNextByte -= numBits;
+ if self.bitsLeftInNextByte == 0 {
+ self.output.push(self.nextByte as u8);
+ self.nextByte = 0;
+ self.bitsLeftInNextByte = 8;
+ }
+ } else {
+ let bitsToWriteNow = self.bitsLeftInNextByte;
+ let numRestOfBits = numBits - bitsToWriteNow;
+ let mask = 0xFF >> (8 - bitsToWriteNow);
+ let valueToWriteNow = (value >> numRestOfBits) & mask;
+ self.write(valueToWriteNow, bitsToWriteNow);
+ self.write(value, numRestOfBits);
+ }
+ }
+
+ pub fn toByteArray(&mut self) -> &Vec {
+ if self.bitsLeftInNextByte < 8 {
+ self.write(0, self.bitsLeftInNextByte);
+ }
+ &self.output
+ }
+}
\ No newline at end of file
diff --git a/src/common/character_set_eci.rs b/src/common/character_set_eci.rs
new file mode 100644
index 0000000..2d8c511
--- /dev/null
+++ b/src/common/character_set_eci.rs
@@ -0,0 +1,289 @@
+
+/*
+ * 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;
+
+// import com.google.zxing.FormatException;
+
+// import java.nio.charset.Charset;
+
+// import java.util.HashMap;
+// import java.util.Map;
+
+use encoding::EncodingRef;
+
+use crate::Exceptions;
+
+/**
+ * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
+ * of ISO 18004.
+ *
+ * @author Sean Owen
+ */
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum CharacterSetECI {
+ // Enum name is a Java encoding valid for java.lang and java.io
+ Cp437, //(new int[]{0,2}),
+ ISO8859_1, //(new int[]{1,3}, "ISO-8859-1"),
+ ISO8859_2, //(4, "ISO-8859-2"),
+ ISO8859_3, //(5, "ISO-8859-3"),
+ ISO8859_4, //(6, "ISO-8859-4"),
+ ISO8859_5, //(7, "ISO-8859-5"),
+ ISO8859_6, //(8, "ISO-8859-6"),
+ ISO8859_7, //(9, "ISO-8859-7"),
+ ISO8859_8, //(10, "ISO-8859-8"),
+ ISO8859_9, //(11, "ISO-8859-9"),
+ ISO8859_10, //(12, "ISO-8859-10"),
+ ISO8859_11, //(13, "ISO-8859-11"),
+ ISO8859_13, //(15, "ISO-8859-13"),
+ ISO8859_14, //(16, "ISO-8859-14"),
+ ISO8859_15, //(17, "ISO-8859-15"),
+ ISO8859_16, //(18, "ISO-8859-16"),
+ SJIS, //(20, "Shift_JIS"),
+ Cp1250, //(21, "windows-1250"),
+ Cp1251, //(22, "windows-1251"),
+ Cp1252, //(23, "windows-1252"),
+ Cp1256, //(24, "windows-1256"),
+ UnicodeBigUnmarked, //(25, "UTF-16BE", "UnicodeBig"),
+ UTF8, //(26, "UTF-8"),
+ ASCII, //(new int[] {27, 170}, "US-ASCII"),
+ Big5, //(28),
+ GB18030, //(29, "GB2312", "EUC_CN", "GBK"),
+ EUC_KR, //(30, "EUC-KR");
+}
+impl CharacterSetECI {
+ // private static final Map VALUE_TO_ECI = new HashMap<>();
+ // private static final Map NAME_TO_ECI = new HashMap<>();
+ // static {
+ // for (CharacterSetECI eci : values()) {
+ // for (int value : eci.values) {
+ // VALUE_TO_ECI.put(value, eci);
+ // }
+ // NAME_TO_ECI.put(eci.name(), eci);
+ // for (String name : eci.otherEncodingNames) {
+ // NAME_TO_ECI.put(name, eci);
+ // }
+ // }
+ // }
+
+ // private final int[] values;
+ // private final String[] otherEncodingNames;
+
+ // CharacterSetECI(int value) {
+ // this(new int[] {value});
+ // }
+
+ // CharacterSetECI(int value, String... otherEncodingNames) {
+ // this.values = new int[] {value};
+ // this.otherEncodingNames = otherEncodingNames;
+ // }
+
+ // CharacterSetECI(int[] values, String... otherEncodingNames) {
+ // this.values = values;
+ // this.otherEncodingNames = otherEncodingNames;
+ // }
+
+ pub fn getValueSelf(&self) -> u32 {
+ Self::getValue(self)
+ }
+
+ pub fn getValue(cs_eci: &CharacterSetECI) -> u32 {
+ match cs_eci {
+ CharacterSetECI::Cp437 => 0,
+ CharacterSetECI::ISO8859_1 => 1,
+ CharacterSetECI::ISO8859_2 => 4,
+ CharacterSetECI::ISO8859_3 => 5,
+ CharacterSetECI::ISO8859_4 => 6,
+ CharacterSetECI::ISO8859_5 => 7,
+ CharacterSetECI::ISO8859_6 => 8,
+ CharacterSetECI::ISO8859_7 => 9,
+ CharacterSetECI::ISO8859_8 => 10,
+ CharacterSetECI::ISO8859_9 => 11,
+ CharacterSetECI::ISO8859_10 => 12,
+ CharacterSetECI::ISO8859_11 => 13,
+ CharacterSetECI::ISO8859_13 => 15,
+ CharacterSetECI::ISO8859_14 => 16,
+ CharacterSetECI::ISO8859_15 => 17,
+ CharacterSetECI::ISO8859_16 => 18,
+ CharacterSetECI::SJIS => 20,
+ CharacterSetECI::Cp1250 => 21,
+ CharacterSetECI::Cp1251 => 22,
+ CharacterSetECI::Cp1252 => 23,
+ CharacterSetECI::Cp1256 => 24,
+ CharacterSetECI::UnicodeBigUnmarked => 25,
+ CharacterSetECI::UTF8 => 26,
+ CharacterSetECI::ASCII => 27,
+ CharacterSetECI::Big5 => 28,
+ CharacterSetECI::GB18030 => 29,
+ CharacterSetECI::EUC_KR => 30,
+ }
+ }
+
+ pub fn getCharset(cs_eci: &CharacterSetECI) -> EncodingRef {
+ let name = match cs_eci {
+ // CharacterSetECI::Cp437 => "CP437",
+ CharacterSetECI::Cp437 => "UTF-8",
+ CharacterSetECI::ISO8859_1 => "ISO-8859-1",
+ CharacterSetECI::ISO8859_2 => "ISO-8859-2",
+ CharacterSetECI::ISO8859_3 => "ISO-8859-3",
+ CharacterSetECI::ISO8859_4 => "ISO-8859-4",
+ CharacterSetECI::ISO8859_5 => "ISO-8859-5",
+ CharacterSetECI::ISO8859_6 => "ISO-8859-6",
+ CharacterSetECI::ISO8859_7 => "ISO-8859-7",
+ CharacterSetECI::ISO8859_8 => "ISO-8859-8",
+ CharacterSetECI::ISO8859_9 => "ISO-8859-9",
+ CharacterSetECI::ISO8859_10 => "ISO-8859-10",
+ CharacterSetECI::ISO8859_11 => "ISO-8859-11",
+ CharacterSetECI::ISO8859_13 => "ISO-8859-13",
+ CharacterSetECI::ISO8859_14 => "ISO-8859-14",
+ CharacterSetECI::ISO8859_15 => "ISO-8859-15",
+ CharacterSetECI::ISO8859_16 => "ISO-8859-16",
+ CharacterSetECI::SJIS => "Shift_JIS",
+ CharacterSetECI::Cp1250 => "windows-1250",
+ CharacterSetECI::Cp1251 => "windows-1251",
+ CharacterSetECI::Cp1252 => "windows-1252",
+ CharacterSetECI::Cp1256 => "windows-1256",
+ CharacterSetECI::UnicodeBigUnmarked => "UTF-16BE",
+ CharacterSetECI::UTF8 => "UTF-8",
+ CharacterSetECI::ASCII => "US-ASCII",
+ CharacterSetECI::Big5 => "Big5",
+ CharacterSetECI::GB18030 => "GB2312",
+ CharacterSetECI::EUC_KR => "EUC-KR",
+ };
+ encoding::label::encoding_from_whatwg_label(name).unwrap()
+ }
+
+ /**
+ * @param charset Java character set object
+ * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
+ * but unsupported
+ */
+ pub fn getCharacterSetECI(charset: EncodingRef) -> Option {
+ let name = if let Some(nm) = charset.whatwg_name() {
+ nm
+ } else {
+ charset.name()
+ };
+ match name {
+ "CP437" => Some(CharacterSetECI::Cp437),
+ "iso-8859-1" => Some(CharacterSetECI::ISO8859_1),
+ "iso-8859-2" => Some(CharacterSetECI::ISO8859_2),
+ "iso-8859-3" => Some(CharacterSetECI::ISO8859_3),
+ "iso-8859-4" => Some(CharacterSetECI::ISO8859_4),
+ "iso-8859-5" => Some(CharacterSetECI::ISO8859_5),
+ "iso-8859-6" => Some(CharacterSetECI::ISO8859_6),
+ "iso-8859-7" => Some(CharacterSetECI::ISO8859_7),
+ "iso-8859-8" => Some(CharacterSetECI::ISO8859_8),
+ "iso-8859-9" => Some(CharacterSetECI::ISO8859_9),
+ "iso-8859-10" => Some(CharacterSetECI::ISO8859_10),
+ "iso-8859-11" => Some(CharacterSetECI::ISO8859_11),
+ "iso-8859-13" => Some(CharacterSetECI::ISO8859_13),
+ "iso-8859-14" => Some(CharacterSetECI::ISO8859_14),
+ "iso-8859-15" => Some(CharacterSetECI::ISO8859_15),
+ "iso-8859-16" => Some(CharacterSetECI::ISO8859_16),
+ "shift_jis" => Some(CharacterSetECI::SJIS),
+ "windows-1250" => Some(CharacterSetECI::Cp1250),
+ "windows-1251" => Some(CharacterSetECI::Cp1251),
+ "windows-1252" => Some(CharacterSetECI::Cp1252),
+ "windows-1256" => Some(CharacterSetECI::Cp1256),
+ "utf-16be" => Some(CharacterSetECI::UnicodeBigUnmarked),
+ "utf-8" => Some(CharacterSetECI::UTF8),
+ "us-ascii" => Some(CharacterSetECI::ASCII),
+ "big5" => Some(CharacterSetECI::Big5),
+ "gb2312" => Some(CharacterSetECI::GB18030),
+ "euc-kr" => Some(CharacterSetECI::EUC_KR),
+ _ => None,
+ }
+ }
+
+ /**
+ * @param value character set ECI value
+ * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but
+ * unsupported
+ * @throws FormatException if ECI value is invalid
+ */
+ pub fn getCharacterSetECIByValue(value: u32) -> Result {
+ match value {
+ 0 | 2 => Ok(CharacterSetECI::Cp437),
+ 1 | 3 => Ok(CharacterSetECI::ISO8859_1),
+ 4 => Ok(CharacterSetECI::ISO8859_2),
+ 5 => Ok(CharacterSetECI::ISO8859_3),
+ 6 => Ok(CharacterSetECI::ISO8859_4),
+ 7 => Ok(CharacterSetECI::ISO8859_5),
+ 8 => Ok(CharacterSetECI::ISO8859_6),
+ 9 => Ok(CharacterSetECI::ISO8859_7),
+ 10 => Ok(CharacterSetECI::ISO8859_8),
+ 11 => Ok(CharacterSetECI::ISO8859_9),
+ 12 => Ok(CharacterSetECI::ISO8859_10),
+ 13 => Ok(CharacterSetECI::ISO8859_11),
+ 15 => Ok(CharacterSetECI::ISO8859_13),
+ 16 => Ok(CharacterSetECI::ISO8859_14),
+ 17 => Ok(CharacterSetECI::ISO8859_15),
+ 18 => Ok(CharacterSetECI::ISO8859_16),
+ 20 => Ok(CharacterSetECI::SJIS),
+ 21 => Ok(CharacterSetECI::Cp1250),
+ 22 => Ok(CharacterSetECI::Cp1251),
+ 23 => Ok(CharacterSetECI::Cp1252),
+ 24 => Ok(CharacterSetECI::Cp1256),
+ 25 => Ok(CharacterSetECI::UnicodeBigUnmarked),
+ 26 => Ok(CharacterSetECI::UTF8),
+ 27 | 170 => Ok(CharacterSetECI::ASCII),
+ 28 => Ok(CharacterSetECI::Big5),
+ 29 => Ok(CharacterSetECI::GB18030),
+ 30 => Ok(CharacterSetECI::EUC_KR),
+ _ => Err(Exceptions::NotFoundException("Bad ECI Value".to_owned())),
+ }
+ }
+
+ /**
+ * @param name character set ECI encoding name
+ * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
+ * but unsupported
+ */
+ pub fn getCharacterSetECIByName(name: &str) -> Option {
+ match name {
+ "CP437" => Some(CharacterSetECI::Cp437),
+ "ISO-8859-1" => Some(CharacterSetECI::ISO8859_1),
+ "ISO-8859-2" => Some(CharacterSetECI::ISO8859_2),
+ "ISO-8859-3" => Some(CharacterSetECI::ISO8859_3),
+ "ISO-8859-4" => Some(CharacterSetECI::ISO8859_4),
+ "ISO-8859-5" => Some(CharacterSetECI::ISO8859_5),
+ "ISO-8859-6" => Some(CharacterSetECI::ISO8859_6),
+ "ISO-8859-7" => Some(CharacterSetECI::ISO8859_7),
+ "ISO-8859-8" => Some(CharacterSetECI::ISO8859_8),
+ "ISO-8859-9" => Some(CharacterSetECI::ISO8859_9),
+ "ISO-8859-10" => Some(CharacterSetECI::ISO8859_10),
+ "ISO-8859-11" => Some(CharacterSetECI::ISO8859_11),
+ "ISO-8859-13" => Some(CharacterSetECI::ISO8859_13),
+ "ISO-8859-14" => Some(CharacterSetECI::ISO8859_14),
+ "ISO-8859-15" => Some(CharacterSetECI::ISO8859_15),
+ "ISO-8859-16" => Some(CharacterSetECI::ISO8859_16),
+ "Shift_JIS" => Some(CharacterSetECI::SJIS),
+ "windows-1250" => Some(CharacterSetECI::Cp1250),
+ "windows-1251" => Some(CharacterSetECI::Cp1251),
+ "windows-1252" => Some(CharacterSetECI::Cp1252),
+ "windows-1256" => Some(CharacterSetECI::Cp1256),
+ "UTF-16BE" => Some(CharacterSetECI::UnicodeBigUnmarked),
+ "UTF-8" => Some(CharacterSetECI::UTF8),
+ "US-ASCII" => Some(CharacterSetECI::ASCII),
+ "Big5" => Some(CharacterSetECI::Big5),
+ "GB2312" => Some(CharacterSetECI::GB18030),
+ "EUC-KR" => Some(CharacterSetECI::EUC_KR),
+ _ => None,
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/decoder_rxing_result.rs b/src/common/decoder_rxing_result.rs
new file mode 100644
index 0000000..9c39c3b
--- /dev/null
+++ b/src/common/decoder_rxing_result.rs
@@ -0,0 +1,209 @@
+
+/*
+ * 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.List;
+
+use std::any::Any;
+
+/**
+ * Encapsulates the result of decoding a matrix of bits. This typically
+ * applies to 2D barcode formats. For now it contains the raw bytes obtained,
+ * as well as a String interpretation of those bytes, if applicable.
+ *
+ * @author Sean Owen
+ */
+pub struct DecoderRXingResult {
+ rawBytes: Vec,
+ numBits: usize,
+ text: String,
+ byteSegments: Vec>,
+ ecLevel: String,
+ errorsCorrected: u64,
+ erasures: u64,
+ other: Box,
+ structuredAppendParity: i32,
+ structuredAppendSequenceNumber: i32,
+ symbologyModifier: u32,
+}
+
+impl DecoderRXingResult {
+ pub fn new(
+ rawBytes: Vec,
+ text: String,
+ byteSegments: Vec>,
+ ecLevel: String,
+ ) -> Self {
+ Self::with_all(rawBytes, text, byteSegments, ecLevel, -2, -2, 0)
+ }
+
+ pub fn with_symbology(
+ rawBytes: Vec,
+ text: String,
+ byteSegments: Vec>,
+ ecLevel: String,
+ symbologyModifier: u32,
+ ) -> Self {
+ Self::with_all(
+ rawBytes,
+ text,
+ byteSegments,
+ ecLevel,
+ -1,
+ -1,
+ symbologyModifier,
+ )
+ }
+
+ pub fn with_sa(
+ rawBytes: Vec,
+ text: String,
+ byteSegments: Vec>,
+ ecLevel: String,
+ saSequence: i32,
+ saParity: i32,
+ ) -> Self {
+ Self::with_all(
+ rawBytes,
+ text,
+ byteSegments,
+ ecLevel,
+ saSequence,
+ saParity,
+ 0,
+ )
+ }
+
+ pub fn with_all(
+ rawBytes: Vec,
+ text: String,
+ byteSegments: Vec>,
+ ecLevel: String,
+ saSequence: i32,
+ saParity: i32,
+ symbologyModifier: u32,
+ ) -> Self {
+ let nb = rawBytes.len();
+ Self {
+ rawBytes,
+ numBits: nb,
+ text,
+ byteSegments,
+ ecLevel,
+ errorsCorrected: 0,
+ erasures: 0,
+ other: Box::new(false),
+ structuredAppendParity: saParity,
+ structuredAppendSequenceNumber: saSequence,
+ symbologyModifier,
+ }
+ }
+
+ /**
+ * @return raw bytes representing the result, or {@code null} if not applicable
+ */
+ pub fn getRawBytes(&self) -> &Vec {
+ &self.rawBytes
+ }
+
+ /**
+ * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length
+ * @since 3.3.0
+ */
+ pub fn getNumBits(&self) -> usize {
+ self.numBits
+ }
+
+ /**
+ * @param numBits overrides the number of bits that are valid in {@link #getRawBytes()}
+ * @since 3.3.0
+ */
+ pub fn setNumBits(&mut self, numBits: usize) {
+ self.numBits = numBits;
+ }
+
+ /**
+ * @return text representation of the result
+ */
+ pub fn getText(&self) -> &str {
+ &self.text
+ }
+
+ /**
+ * @return list of byte segments in the result, or {@code null} if not applicable
+ */
+ pub fn getByteSegments(&self) -> &Vec> {
+ &self.byteSegments
+ }
+
+ /**
+ * @return name of error correction level used, or {@code null} if not applicable
+ */
+ pub fn getECLevel(&self) -> &str {
+ &self.ecLevel
+ }
+
+ /**
+ * @return number of errors corrected, or {@code null} if not applicable
+ */
+ pub fn getErrorsCorrected(&self) -> u64 {
+ self.errorsCorrected
+ }
+
+ pub fn setErrorsCorrected(&mut self, errorsCorrected: u64) {
+ self.errorsCorrected = errorsCorrected;
+ }
+
+ /**
+ * @return number of erasures corrected, or {@code null} if not applicable
+ */
+ pub fn getErasures(&self) -> u64 {
+ self.erasures
+ }
+
+ pub fn setErasures(&mut self, erasures: u64) {
+ self.erasures = erasures
+ }
+
+ /**
+ * @return arbitrary additional metadata
+ */
+ pub fn getOther(&self) -> &Box {
+ &self.other
+ }
+
+ pub fn setOther(&mut self, other: Box) {
+ self.other = other
+ }
+
+ pub fn hasStructuredAppend(&self) -> bool {
+ self.structuredAppendParity >= 0 && self.structuredAppendSequenceNumber >= 0
+ }
+
+ pub fn getStructuredAppendParity(&self) -> i32 {
+ self.structuredAppendParity
+ }
+
+ pub fn getStructuredAppendSequenceNumber(&self) -> i32 {
+ self.structuredAppendSequenceNumber
+ }
+
+ pub fn getSymbologyModifier(&self) -> u32 {
+ self.symbologyModifier
+ }
+}
\ No newline at end of file
diff --git a/src/common/default_grid_sampler.rs b/src/common/default_grid_sampler.rs
new file mode 100644
index 0000000..eeaf523
--- /dev/null
+++ b/src/common/default_grid_sampler.rs
@@ -0,0 +1,121 @@
+
+/*
+ * 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 com.google.zxing.NotFoundException;
+
+use crate::Exceptions;
+
+use super::{BitMatrix, PerspectiveTransform, GridSampler};
+
+/**
+ * @author Sean Owen
+ */
+pub struct DefaultGridSampler {}
+
+impl GridSampler for DefaultGridSampler {
+ fn sample_grid_detailed(
+ &self,
+ image: &BitMatrix,
+ dimensionX: u32,
+ dimensionY: u32,
+ p1ToX: f32,
+ p1ToY: f32,
+ p2ToX: f32,
+ p2ToY: f32,
+ p3ToX: f32,
+ p3ToY: f32,
+ p4ToX: f32,
+ p4ToY: f32,
+ p1FromX: f32,
+ p1FromY: f32,
+ p2FromX: f32,
+ p2FromY: f32,
+ p3FromX: f32,
+ p3FromY: f32,
+ p4FromX: f32,
+ p4FromY: f32,
+ ) -> Result {
+ let transform = PerspectiveTransform::quadrilateralToQuadrilateral(
+ p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX,
+ p2FromY, p3FromX, p3FromY, p4FromX, p4FromY,
+ );
+
+ self.sample_grid(image, dimensionX, dimensionY, &transform)
+ }
+
+ fn sample_grid(
+ &self,
+ image: &BitMatrix,
+ dimensionX: u32,
+ dimensionY: u32,
+ transform: &PerspectiveTransform,
+ ) -> Result {
+ if dimensionX == 0 || dimensionY == 0 {
+ return Err(Exceptions::NotFoundException(
+ "getNotFoundInstance".to_owned(),
+ ));
+ }
+ let mut bits = BitMatrix::new(dimensionX, dimensionY)?;
+ let mut points = vec![0_f32; 2 * dimensionX as usize];
+ for y in 0..dimensionY {
+ // for (int y = 0; y < dimensionY; y++) {
+ let max = points.len();
+ let i_value = y as f32 + 0.5f32;
+ let mut x = 0;
+ while x < max {
+ // for (int x = 0; x < max; x += 2) {
+ points[x] = (x as f32 / 2.0) + 0.5f32;
+ points[x + 1] = i_value;
+ x += 2;
+ }
+ transform.transform_points_single(&mut points);
+ // Quick check to see if points transformed to something inside the image;
+ // sufficient to check the endpoints
+ self.checkAndNudgePoints(image, &mut points)?;
+ // try {
+ let mut x = 0;
+ while x < max {
+ // for (int x = 0; x < max; x += 2) {
+ if points[x].floor() as u32 >= image.getWidth()
+ || points[x + 1].floor() as u32 >= image.getHeight()
+ {
+ return Err(Exceptions::NotFoundException(
+ "index out of bounds, see documentation in file for explanation".to_owned(),
+ ));
+ }
+ if image.get(points[x].floor() as u32, points[x + 1].floor() as u32) {
+ // Black(-ish) pixel
+ bits.set(x as u32 / 2, y);
+ }
+ x += 2;
+ }
+ // } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
+ // // transform gets "twisted" such that it maps a straight line of points to a set of points
+ // // whose endpoints are in bounds, but others are not. There is probably some mathematical
+ // // way to detect this about the transformation that I don't know yet.
+ // // This results in an ugly runtime exception despite our clever checks above -- can't have
+ // // that. We could check each point's coordinates but that feels duplicative. We settle for
+ // // catching and wrapping ArrayIndexOutOfBoundsException.
+ // throw NotFoundException.getNotFoundInstance();
+ // }
+ }
+ return Ok(bits);
+ }
+}
\ No newline at end of file
diff --git a/src/common/detector/mod.rs b/src/common/detector/mod.rs
index cd93b16..7deada3 100644
--- a/src/common/detector/mod.rs
+++ b/src/common/detector/mod.rs
@@ -2,697 +2,8 @@ pub mod MathUtils;
use crate::common::BitMatrix;
use crate::{Exceptions, RXingResultPoint, ResultPoint};
-/*
- * Copyright 2009 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.
- */
+mod monochrome_rectangle_detector;
+pub use monochrome_rectangle_detector::*;
-//package com.google.zxing.common.detector;
-
-/**
- * A somewhat generic detector that looks for a barcode-like rectangular region within an image.
- * It looks within a mostly white region of an image for a region of black and white, but mostly
- * black. It returns the four corners of the region, as best it can determine.
- *
- * @author Sean Owen
- * @deprecated without replacement since 3.3.0
- */
-const MAX_MODULES: i32 = 32;
-#[deprecated]
-pub struct MonochromeRectangleDetector {
- image: BitMatrix,
-}
-
-impl MonochromeRectangleDetector {
- pub fn new(image: &BitMatrix) -> Self {
- Self { image: image.clone() }
- }
-
- /**
- * Detects a rectangular region of black and white -- mostly black -- with a region of mostly
- * white, in an image.
- *
- * @return {@link RXingResultPoint}[] describing the corners of the rectangular region. The first and
- * last points are opposed on the diagonal, as are the second and third. The first point will be
- * the topmost point and the last, the bottommost. The second point will be leftmost and the
- * third, the rightmost
- * @throws NotFoundException if no Data Matrix Code can be found
- */
- pub fn detect(&self) -> Result, Exceptions> {
- let height = self.image.getHeight() as i32;
- let width = self.image.getWidth() as i32;
- let halfHeight= height / 2;
- let halfWidth = width / 2;
- let deltaY = 1.max(height as i32 / (MAX_MODULES * 8));
- let deltaX = 1.max(width as i32 / (MAX_MODULES * 8));
-
- let mut top = 0;
- let mut bottom = height;
- let mut left = 0;
- let mut right = width;
- let mut pointA = self.findCornerFromCenter(
- halfWidth,
- 0,
- left,
- right,
- halfHeight,
- -deltaY,
- top,
- bottom,
- halfWidth / 2,
- )?;
- top = (pointA.getY() - 1f32) as i32;
- let pointB = self.findCornerFromCenter(
- halfWidth,
- -deltaX,
- left,
- right,
- halfHeight,
- 0,
- top,
- bottom,
- halfHeight / 2,
- )?;
- left = (pointB.getX() - 1f32) as i32;
- let pointC = self.findCornerFromCenter(
- halfWidth,
- deltaX,
- left,
- right,
- halfHeight,
- 0,
- top,
- bottom,
- halfHeight / 2,
- )?;
- right = (pointC.getX() + 1f32) as i32;
- let pointD = self.findCornerFromCenter(
- halfWidth,
- 0,
- left,
- right,
- halfHeight,
- deltaY,
- top,
- bottom,
- halfWidth / 2,
- )?;
- bottom = (pointD.getY() + 1f32) as i32;
-
- // Go try to find point A again with better information -- might have been off at first.
- pointA = self.findCornerFromCenter(
- halfWidth,
- 0,
- left,
- right,
- halfHeight,
- -deltaY,
- top,
- bottom,
- halfWidth / 4,
- )?;
-
- return Ok(vec![pointA, pointB, pointC, pointD]);
- }
-
- /**
- * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
- * point which should be within the barcode.
- *
- * @param centerX center's x component (horizontal)
- * @param deltaX same as deltaY but change in x per step instead
- * @param left minimum value of x
- * @param right maximum value of x
- * @param centerY center's y component (vertical)
- * @param deltaY change in y per step. If scanning up this is negative; down, positive;
- * left or right, 0
- * @param top minimum value of y to search through (meaningless when di == 0)
- * @param bottom maximum value of y
- * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
- * the barcode
- * @return a {@link RXingResultPoint} encapsulating the corner that was found
- * @throws NotFoundException if such a point cannot be found
- */
- fn findCornerFromCenter(
- &self,
- centerX: i32,
- deltaX: i32,
- left: i32,
- right: i32,
- centerY: i32,
- deltaY: i32,
- top: i32,
- bottom: i32,
- maxWhiteRun: i32,
- ) -> Result {
- let mut lastRange_z: Option> = None;
- let mut y: i32 = centerY;
- let mut x: i32 = centerX;
- while y < bottom && y >= top && x < right && x >= left {
- let range: Option>;
- if deltaX == 0 {
- // horizontal slices, up and down
- range = self.blackWhiteRange(y, maxWhiteRun, left, right, true);
- } else {
- // vertical slices, left and right
- range = self.blackWhiteRange(x, maxWhiteRun, top, bottom, false);
- }
- if range.is_none() {
- if let Some(lastRange) = lastRange_z {
- // lastRange was found
- if deltaX == 0 {
- let lastY = y - deltaY;
- if lastRange[0] < centerX {
- if lastRange[1] > centerX {
- // straddle, choose one or the other based on direction
- return Ok(RXingResultPoint::new(
- lastRange[if deltaY > 0 { 0 } else { 1 }] as f32,
- lastY as f32,
- ));
- }
- return Ok(RXingResultPoint::new(
- lastRange[0] as f32,
- lastY as f32,
- ));
- } else {
- return Ok(RXingResultPoint::new(
- lastRange[1] as f32,
- lastY as f32,
- ));
- }
- } else {
- let lastX = x - deltaX;
- if lastRange[0] < centerY {
- if lastRange[1] > centerY {
- return Ok(RXingResultPoint::new(
- lastX as f32,
- lastRange[if deltaX < 0 { 0 } else { 1 }] as f32,
- ));
- }
- return Ok(RXingResultPoint::new(
- lastX as f32,
- lastRange[0] as f32,
- ));
- } else {
- return Ok(RXingResultPoint::new(
- lastX as f32,
- lastRange[1] as f32,
- ));
- }
- }
- }}else {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
- lastRange_z = range;
- y += deltaY;
- x += deltaX
- }
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- /**
- * Computes the start and end of a region of pixels, either horizontally or vertically, that could
- * be part of a Data Matrix barcode.
- *
- * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
- * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
- * @param maxWhiteRun largest run of white pixels that can still be considered part of the
- * barcode region
- * @param minDim minimum pixel location, horizontally or vertically, to consider
- * @param maxDim maximum pixel location, horizontally or vertically, to consider
- * @param horizontal if true, we're scanning left-right, instead of up-down
- * @return int[] with start and end of found range, or null if no such range is found
- * (e.g. only white was found)
- */
- fn blackWhiteRange(
- &self,
- fixedDimension: i32,
- maxWhiteRun: i32,
- minDim: i32,
- maxDim: i32,
- horizontal: bool,
- ) -> Option> {
- let center = (minDim + maxDim) / 2;
-
- // Scan left/up first
- let mut start = center;
- while (start >= minDim) {
- if if horizontal {
- self.image.get(start as u32, fixedDimension as u32)
- } else {
- self.image.get(fixedDimension as u32, start as u32)
- } {
- start = start - 1;
- } else {
- let whiteRunStart = start;
- start = start - 1;
- while start >= minDim
- && !(if horizontal {
- self.image.get(start as u32, fixedDimension as u32)
- } else {
- self.image.get(fixedDimension as u32, start as u32)
- })
- {
- start = start - 1;
- }
- let whiteRunSize = whiteRunStart - start;
- if start < minDim || whiteRunSize > maxWhiteRun {
- start = whiteRunStart;
- break;
- }
- }
- }
- start = start + 1;
-
- // Then try right/down
- let mut end = center;
- while (end < maxDim) {
- if if horizontal {
- self.image.get(end as u32, fixedDimension as u32)
- } else {
- self.image.get(fixedDimension as u32, end as u32)
- } {
- end = end + 1;
- } else {
- let whiteRunStart = end;
- end = end + 1;
- while end < maxDim
- && !(if horizontal {
- self.image.get(end as u32, fixedDimension as u32)
- } else {
- self.image.get(fixedDimension as u32, end as u32)
- })
- {
- end = end + 1;
- }
- let whiteRunSize = end - whiteRunStart;
- if end >= maxDim || whiteRunSize > maxWhiteRun {
- end = whiteRunStart;
- break;
- }
- }
- }
- end = end - 1;
-
- return if end > start {
- Some(vec![start, end])
- } else {
- None
- };
- }
-}
-
-/*
- * Copyright 2010 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.detector;
-
-/**
- *
- * Detects a candidate barcode-like rectangular region within an image. It
- * starts around the center of the image, increases the size of the candidate
- * region until it finds a white rectangular region. By keeping track of the
- * last black points it encountered, it determines the corners of the barcode.
- *
- *
- * @author David Olivier
- */
-const INIT_SIZE: i32 = 10;
-const CORR: i32 = 1;
-pub struct WhiteRectangleDetector {
- image: BitMatrix,
- height: i32,
- width: i32,
- leftInit: i32,
- rightInit: i32,
- downInit: i32,
- upInit: i32,
-}
-
-impl WhiteRectangleDetector {
- pub fn new_from_image(image: &BitMatrix) -> Result {
- Self::new(
- image,
- INIT_SIZE,
- image.getWidth() as i32 / 2,
- image.getHeight() as i32 / 2,
- )
- }
-
- /**
- * @param image barcode image to find a rectangle in
- * @param initSize initial size of search area around center
- * @param x x position of search center
- * @param y y position of search center
- * @throws NotFoundException if image is too small to accommodate {@code initSize}
- */
- pub fn new(
- image: &BitMatrix,
- initSize: i32,
- x: i32,
- y: i32,
- ) -> Result {
-
- let halfsize = initSize / 2;
-
- let leftInit = x - halfsize;
- let rightInit = x + halfsize;
- let upInit = y - halfsize;
- let downInit = y + halfsize;
-
- if upInit < 0
- || leftInit < 0
- || downInit >= image.getHeight() as i32
- || rightInit >= image.getWidth() as i32
- {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- Ok(Self{
- image: image.clone(),
- height: image.getHeight() as i32,
- width: image.getWidth() as i32,
- leftInit: leftInit,
- rightInit: rightInit,
- downInit: downInit,
- upInit: upInit,
- })
- }
-
- /**
- *
- * Detects a candidate barcode-like rectangular region within an image. It
- * starts around the center of the image, increases the size of the candidate
- * region until it finds a white rectangular region.
- *
- *
- * @return {@link RXingResultPoint}[] describing the corners of the rectangular
- * region. The first and last points are opposed on the diagonal, as
- * are the second and third. The first point will be the topmost
- * point and the last, the bottommost. The second point will be
- * leftmost and the third, the rightmost
- * @throws NotFoundException if no Data Matrix Code can be found
- */
- pub fn detect(&self) -> Result, Exceptions> {
- let mut left: i32 = self.leftInit;
- let mut right: i32 = self.rightInit;
- let mut up: i32 = self.upInit;
- let mut down: i32 = self.downInit;
- let mut size_exceeded = false;
- let mut a_black_point_found_on_border = true;
-
- let mut at_least_one_black_point_found_on_right = false;
- let mut at_least_one_black_point_found_on_bottom = false;
- let mut at_least_one_black_point_found_on_left = false;
- let mut at_least_one_black_point_found_on_top = false;
-
- while a_black_point_found_on_border {
- a_black_point_found_on_border = false;
-
- // .....
- // . |
- // .....
- let mut right_border_not_white = true;
- while (right_border_not_white || !at_least_one_black_point_found_on_right) && right < self.width {
- right_border_not_white = self.contains_black_point(up, down, right, false);
- if right_border_not_white {
- right += 1;
- a_black_point_found_on_border = true;
- at_least_one_black_point_found_on_right = true;
- } else if !at_least_one_black_point_found_on_right {
- right += 1;
- }
- }
-
- if right >= self.width {
- size_exceeded = true;
- break;
- }
-
- // .....
- // . .
- // .___.
- let mut bottom_border_not_white = true;
- while (bottom_border_not_white || !at_least_one_black_point_found_on_bottom) && down < self.height
- {
- bottom_border_not_white = self.contains_black_point(left, right, down, true);
- if bottom_border_not_white {
- down += 1;
- a_black_point_found_on_border = true;
- at_least_one_black_point_found_on_bottom = true;
- } else if !at_least_one_black_point_found_on_bottom {
- down += 1;
- }
- }
-
- if down >= self.height {
- size_exceeded = true;
- break;
- }
-
- // .....
- // | .
- // .....
- let mut left_border_not_white = true;
- while (left_border_not_white || !at_least_one_black_point_found_on_left) && left >= 0 {
- left_border_not_white = self.contains_black_point(up, down, left, false);
- if left_border_not_white {
- left -= 1;
- a_black_point_found_on_border = true;
- at_least_one_black_point_found_on_left = true;
- } else if !at_least_one_black_point_found_on_left {
- left -= 1;
- }
- }
-
- if left < 0 {
- size_exceeded = true;
- break;
- }
-
- // .___.
- // . .
- // .....
- let mut top_border_not_white = true;
- while (top_border_not_white || !at_least_one_black_point_found_on_top) && up >= 0 {
- top_border_not_white = self.contains_black_point(left, right, up, true);
- if top_border_not_white {
- up -= 1;
- a_black_point_found_on_border = true;
- at_least_one_black_point_found_on_top = true;
- } else if !at_least_one_black_point_found_on_top {
- up -= 1;
- }
- }
-
- if up < 0 {
- size_exceeded = true;
- break;
- }
- }
-
- if !size_exceeded {
- let max_size = right - left;
-
- let mut z: Option = None;
- let mut i = 1;
- while z.is_none() && i < max_size {
- //for (int i = 1; z == null && i < maxSize; i++) {
- z = self.get_black_point_on_segment(
- left as f32,
- (down - i) as f32,
- (left + i) as f32,
- down as f32,
- );
- i += 1;
- }
-
- if z.is_none() {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- let mut t: Option = None;
- //go down right
- let mut i = 1;
- while t.is_none() && i < max_size {
- //for (int i = 1; t == null && i < maxSize; i++) {
- t = self.get_black_point_on_segment(
- left as f32,
- (up + i) as f32,
- (left + i) as f32,
- up as f32,
- );
- i += 1;
- }
-
- if t.is_none() {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- let mut x: Option = None;
- //go down left
- let mut i = 1;
- while x.is_none() && i < max_size {
- //for (int i = 1; x == null && i < maxSize; i++) {
- x = self.get_black_point_on_segment(
- right as f32,
- (up + i) as f32,
- (right - i) as f32,
- up as f32,
- );
- i += 1;
- }
-
- if x.is_none() {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- let mut y: Option = None;
- //go up left
- let mut i = 1;
- while y.is_none() && i < max_size {
- //for (int i = 1; y == null && i < maxSize; i++) {
- y = self.get_black_point_on_segment(
- right as f32,
- (down - i) as f32,
- (right - i) as f32,
- down as f32,
- );
- i += 1;
- }
-
- if y.is_none() {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
-
- return Ok(self.center_edges(&y.unwrap(), &z.unwrap(), &x.unwrap(), &t.unwrap()));
- } else {
- return Err(Exceptions::NotFoundException("".to_owned()));
- }
- }
-
- fn get_black_point_on_segment(
- &self,
- a_x: f32,
- a_y: f32,
- b_x: f32,
- b_y: f32,
- ) -> Option {
- let dist = MathUtils::round(MathUtils::distance_float(a_x, a_y, b_x, b_y));
- let x_step: f32 = (b_x - a_x) / dist as f32;
- let y_step: f32 = (b_y - a_y) / dist as f32;
-
- for i in 0..dist {
- let x = MathUtils::round(a_x + i as f32 * x_step);
- let y = MathUtils::round(a_y + i as f32 * y_step);
- if self.image.get(x as u32, y as u32) {
- return Some(RXingResultPoint::new(x as f32, y as f32));
- }
- }
- return None;
- }
-
- /**
- * recenters the points of a constant distance towards the center
- *
- * @param y bottom most point
- * @param z left most point
- * @param x right most point
- * @param t top most point
- * @return {@link RXingResultPoint}[] describing the corners of the rectangular
- * region. The first and last points are opposed on the diagonal, as
- * are the second and third. The first point will be the topmost
- * point and the last, the bottommost. The second point will be
- * leftmost and the third, the rightmost
- */
- fn center_edges(
- &self,
- y: &RXingResultPoint,
- z: &RXingResultPoint,
- x: &RXingResultPoint,
- t: &RXingResultPoint,
- ) -> Vec {
- //
- // t t
- // z x
- // x OR z
- // y y
- //
-
- let yi = y.getX();
- let yj = y.getY();
- let zi = z.getX();
- let zj = z.getY();
- let xi = x.getX();
- let xj = x.getY();
- let ti = t.getX();
- let tj = t.getY();
-
- if yi < self.width as f32 / 2.0f32 {
- return vec![
- RXingResultPoint::new(ti - CORR as f32, tj + CORR as f32),
- RXingResultPoint::new(zi + CORR as f32, zj + CORR as f32),
- RXingResultPoint::new(xi - CORR as f32, xj - CORR as f32),
- RXingResultPoint::new(yi + CORR as f32, yj - CORR as f32),
- ];
- } else {
- return vec![
- RXingResultPoint::new(ti + CORR as f32, tj + CORR as f32),
- RXingResultPoint::new(zi + CORR as f32, zj - CORR as f32),
- RXingResultPoint::new(xi - CORR as f32, xj + CORR as f32),
- RXingResultPoint::new(yi - CORR as f32, yj - CORR as f32),
- ];
- }
- }
-
- /**
- * Determines whether a segment contains a black point
- *
- * @param a min value of the scanned coordinate
- * @param b max value of the scanned coordinate
- * @param fixed value of fixed coordinate
- * @param horizontal set to true if scan must be horizontal, false if vertical
- * @return true if a black point has been found, else false.
- */
- fn contains_black_point(&self, a: i32, b: i32, fixed: i32, horizontal: bool) -> bool {
- if horizontal {
- for x in a..=b {
- if self.image.get(x as u32, fixed as u32) {
- return true;
- }
- }
- } else {
- for y in a..=b {
- if self.image.get(fixed as u32, y as u32) {
- return true;
- }
- }
- }
-
- return false;
- }
-}
+mod white_rectangle_detector;
+pub use white_rectangle_detector::*;
\ No newline at end of file
diff --git a/src/common/detector/monochrome_rectangle_detector.rs b/src/common/detector/monochrome_rectangle_detector.rs
new file mode 100644
index 0000000..d856e9f
--- /dev/null
+++ b/src/common/detector/monochrome_rectangle_detector.rs
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2009 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.detector;
+
+use crate::{Exceptions, RXingResultPoint, common::BitMatrix, ResultPoint};
+
+/**
+ * A somewhat generic detector that looks for a barcode-like rectangular region within an image.
+ * It looks within a mostly white region of an image for a region of black and white, but mostly
+ * black. It returns the four corners of the region, as best it can determine.
+ *
+ * @author Sean Owen
+ * @deprecated without replacement since 3.3.0
+ */
+const MAX_MODULES: i32 = 32;
+#[deprecated]
+pub struct MonochromeRectangleDetector {
+ image: BitMatrix,
+}
+
+impl MonochromeRectangleDetector {
+ pub fn new(image: &BitMatrix) -> Self {
+ Self { image: image.clone() }
+ }
+
+ /**
+ * Detects a rectangular region of black and white -- mostly black -- with a region of mostly
+ * white, in an image.
+ *
+ * @return {@link RXingResultPoint}[] describing the corners of the rectangular region. The first and
+ * last points are opposed on the diagonal, as are the second and third. The first point will be
+ * the topmost point and the last, the bottommost. The second point will be leftmost and the
+ * third, the rightmost
+ * @throws NotFoundException if no Data Matrix Code can be found
+ */
+ pub fn detect(&self) -> Result, Exceptions> {
+ let height = self.image.getHeight() as i32;
+ let width = self.image.getWidth() as i32;
+ let halfHeight= height / 2;
+ let halfWidth = width / 2;
+ let deltaY = 1.max(height as i32 / (MAX_MODULES * 8));
+ let deltaX = 1.max(width as i32 / (MAX_MODULES * 8));
+
+ let mut top = 0;
+ let mut bottom = height;
+ let mut left = 0;
+ let mut right = width;
+ let mut pointA = self.findCornerFromCenter(
+ halfWidth,
+ 0,
+ left,
+ right,
+ halfHeight,
+ -deltaY,
+ top,
+ bottom,
+ halfWidth / 2,
+ )?;
+ top = (pointA.getY() - 1f32) as i32;
+ let pointB = self.findCornerFromCenter(
+ halfWidth,
+ -deltaX,
+ left,
+ right,
+ halfHeight,
+ 0,
+ top,
+ bottom,
+ halfHeight / 2,
+ )?;
+ left = (pointB.getX() - 1f32) as i32;
+ let pointC = self.findCornerFromCenter(
+ halfWidth,
+ deltaX,
+ left,
+ right,
+ halfHeight,
+ 0,
+ top,
+ bottom,
+ halfHeight / 2,
+ )?;
+ right = (pointC.getX() + 1f32) as i32;
+ let pointD = self.findCornerFromCenter(
+ halfWidth,
+ 0,
+ left,
+ right,
+ halfHeight,
+ deltaY,
+ top,
+ bottom,
+ halfWidth / 2,
+ )?;
+ bottom = (pointD.getY() + 1f32) as i32;
+
+ // Go try to find point A again with better information -- might have been off at first.
+ pointA = self.findCornerFromCenter(
+ halfWidth,
+ 0,
+ left,
+ right,
+ halfHeight,
+ -deltaY,
+ top,
+ bottom,
+ halfWidth / 4,
+ )?;
+
+ return Ok(vec![pointA, pointB, pointC, pointD]);
+ }
+
+ /**
+ * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
+ * point which should be within the barcode.
+ *
+ * @param centerX center's x component (horizontal)
+ * @param deltaX same as deltaY but change in x per step instead
+ * @param left minimum value of x
+ * @param right maximum value of x
+ * @param centerY center's y component (vertical)
+ * @param deltaY change in y per step. If scanning up this is negative; down, positive;
+ * left or right, 0
+ * @param top minimum value of y to search through (meaningless when di == 0)
+ * @param bottom maximum value of y
+ * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
+ * the barcode
+ * @return a {@link RXingResultPoint} encapsulating the corner that was found
+ * @throws NotFoundException if such a point cannot be found
+ */
+ fn findCornerFromCenter(
+ &self,
+ centerX: i32,
+ deltaX: i32,
+ left: i32,
+ right: i32,
+ centerY: i32,
+ deltaY: i32,
+ top: i32,
+ bottom: i32,
+ maxWhiteRun: i32,
+ ) -> Result {
+ let mut lastRange_z: Option> = None;
+ let mut y: i32 = centerY;
+ let mut x: i32 = centerX;
+ while y < bottom && y >= top && x < right && x >= left {
+ let range: Option>;
+ if deltaX == 0 {
+ // horizontal slices, up and down
+ range = self.blackWhiteRange(y, maxWhiteRun, left, right, true);
+ } else {
+ // vertical slices, left and right
+ range = self.blackWhiteRange(x, maxWhiteRun, top, bottom, false);
+ }
+ if range.is_none() {
+ if let Some(lastRange) = lastRange_z {
+ // lastRange was found
+ if deltaX == 0 {
+ let lastY = y - deltaY;
+ if lastRange[0] < centerX {
+ if lastRange[1] > centerX {
+ // straddle, choose one or the other based on direction
+ return Ok(RXingResultPoint::new(
+ lastRange[if deltaY > 0 { 0 } else { 1 }] as f32,
+ lastY as f32,
+ ));
+ }
+ return Ok(RXingResultPoint::new(
+ lastRange[0] as f32,
+ lastY as f32,
+ ));
+ } else {
+ return Ok(RXingResultPoint::new(
+ lastRange[1] as f32,
+ lastY as f32,
+ ));
+ }
+ } else {
+ let lastX = x - deltaX;
+ if lastRange[0] < centerY {
+ if lastRange[1] > centerY {
+ return Ok(RXingResultPoint::new(
+ lastX as f32,
+ lastRange[if deltaX < 0 { 0 } else { 1 }] as f32,
+ ));
+ }
+ return Ok(RXingResultPoint::new(
+ lastX as f32,
+ lastRange[0] as f32,
+ ));
+ } else {
+ return Ok(RXingResultPoint::new(
+ lastX as f32,
+ lastRange[1] as f32,
+ ));
+ }
+ }
+ }}else {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+ lastRange_z = range;
+ y += deltaY;
+ x += deltaX
+ }
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ /**
+ * Computes the start and end of a region of pixels, either horizontally or vertically, that could
+ * be part of a Data Matrix barcode.
+ *
+ * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
+ * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
+ * @param maxWhiteRun largest run of white pixels that can still be considered part of the
+ * barcode region
+ * @param minDim minimum pixel location, horizontally or vertically, to consider
+ * @param maxDim maximum pixel location, horizontally or vertically, to consider
+ * @param horizontal if true, we're scanning left-right, instead of up-down
+ * @return int[] with start and end of found range, or null if no such range is found
+ * (e.g. only white was found)
+ */
+ fn blackWhiteRange(
+ &self,
+ fixedDimension: i32,
+ maxWhiteRun: i32,
+ minDim: i32,
+ maxDim: i32,
+ horizontal: bool,
+ ) -> Option> {
+ let center = (minDim + maxDim) / 2;
+
+ // Scan left/up first
+ let mut start = center;
+ while (start >= minDim) {
+ if if horizontal {
+ self.image.get(start as u32, fixedDimension as u32)
+ } else {
+ self.image.get(fixedDimension as u32, start as u32)
+ } {
+ start = start - 1;
+ } else {
+ let whiteRunStart = start;
+ start = start - 1;
+ while start >= minDim
+ && !(if horizontal {
+ self.image.get(start as u32, fixedDimension as u32)
+ } else {
+ self.image.get(fixedDimension as u32, start as u32)
+ })
+ {
+ start = start - 1;
+ }
+ let whiteRunSize = whiteRunStart - start;
+ if start < minDim || whiteRunSize > maxWhiteRun {
+ start = whiteRunStart;
+ break;
+ }
+ }
+ }
+ start = start + 1;
+
+ // Then try right/down
+ let mut end = center;
+ while (end < maxDim) {
+ if if horizontal {
+ self.image.get(end as u32, fixedDimension as u32)
+ } else {
+ self.image.get(fixedDimension as u32, end as u32)
+ } {
+ end = end + 1;
+ } else {
+ let whiteRunStart = end;
+ end = end + 1;
+ while end < maxDim
+ && !(if horizontal {
+ self.image.get(end as u32, fixedDimension as u32)
+ } else {
+ self.image.get(fixedDimension as u32, end as u32)
+ })
+ {
+ end = end + 1;
+ }
+ let whiteRunSize = end - whiteRunStart;
+ if end >= maxDim || whiteRunSize > maxWhiteRun {
+ end = whiteRunStart;
+ break;
+ }
+ }
+ }
+ end = end - 1;
+
+ return if end > start {
+ Some(vec![start, end])
+ } else {
+ None
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/common/detector/white_rectangle_detector.rs b/src/common/detector/white_rectangle_detector.rs
new file mode 100644
index 0000000..f9cd1d9
--- /dev/null
+++ b/src/common/detector/white_rectangle_detector.rs
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2010 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.detector;
+
+use crate::{RXingResultPoint, Exceptions, common::BitMatrix, ResultPoint};
+
+use super::MathUtils;
+
+/**
+ *
+ * Detects a candidate barcode-like rectangular region within an image. It
+ * starts around the center of the image, increases the size of the candidate
+ * region until it finds a white rectangular region. By keeping track of the
+ * last black points it encountered, it determines the corners of the barcode.
+ *
+ *
+ * @author David Olivier
+ */
+const INIT_SIZE: i32 = 10;
+const CORR: i32 = 1;
+pub struct WhiteRectangleDetector {
+ image: BitMatrix,
+ height: i32,
+ width: i32,
+ leftInit: i32,
+ rightInit: i32,
+ downInit: i32,
+ upInit: i32,
+}
+
+impl WhiteRectangleDetector {
+ pub fn new_from_image(image: &BitMatrix) -> Result {
+ Self::new(
+ image,
+ INIT_SIZE,
+ image.getWidth() as i32 / 2,
+ image.getHeight() as i32 / 2,
+ )
+ }
+
+ /**
+ * @param image barcode image to find a rectangle in
+ * @param initSize initial size of search area around center
+ * @param x x position of search center
+ * @param y y position of search center
+ * @throws NotFoundException if image is too small to accommodate {@code initSize}
+ */
+ pub fn new(
+ image: &BitMatrix,
+ initSize: i32,
+ x: i32,
+ y: i32,
+ ) -> Result {
+
+ let halfsize = initSize / 2;
+
+ let leftInit = x - halfsize;
+ let rightInit = x + halfsize;
+ let upInit = y - halfsize;
+ let downInit = y + halfsize;
+
+ if upInit < 0
+ || leftInit < 0
+ || downInit >= image.getHeight() as i32
+ || rightInit >= image.getWidth() as i32
+ {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ Ok(Self{
+ image: image.clone(),
+ height: image.getHeight() as i32,
+ width: image.getWidth() as i32,
+ leftInit: leftInit,
+ rightInit: rightInit,
+ downInit: downInit,
+ upInit: upInit,
+ })
+ }
+
+ /**
+ *
+ * Detects a candidate barcode-like rectangular region within an image. It
+ * starts around the center of the image, increases the size of the candidate
+ * region until it finds a white rectangular region.
+ *
+ *
+ * @return {@link RXingResultPoint}[] describing the corners of the rectangular
+ * region. The first and last points are opposed on the diagonal, as
+ * are the second and third. The first point will be the topmost
+ * point and the last, the bottommost. The second point will be
+ * leftmost and the third, the rightmost
+ * @throws NotFoundException if no Data Matrix Code can be found
+ */
+ pub fn detect(&self) -> Result, Exceptions> {
+ let mut left: i32 = self.leftInit;
+ let mut right: i32 = self.rightInit;
+ let mut up: i32 = self.upInit;
+ let mut down: i32 = self.downInit;
+ let mut size_exceeded = false;
+ let mut a_black_point_found_on_border = true;
+
+ let mut at_least_one_black_point_found_on_right = false;
+ let mut at_least_one_black_point_found_on_bottom = false;
+ let mut at_least_one_black_point_found_on_left = false;
+ let mut at_least_one_black_point_found_on_top = false;
+
+ while a_black_point_found_on_border {
+ a_black_point_found_on_border = false;
+
+ // .....
+ // . |
+ // .....
+ let mut right_border_not_white = true;
+ while (right_border_not_white || !at_least_one_black_point_found_on_right) && right < self.width {
+ right_border_not_white = self.contains_black_point(up, down, right, false);
+ if right_border_not_white {
+ right += 1;
+ a_black_point_found_on_border = true;
+ at_least_one_black_point_found_on_right = true;
+ } else if !at_least_one_black_point_found_on_right {
+ right += 1;
+ }
+ }
+
+ if right >= self.width {
+ size_exceeded = true;
+ break;
+ }
+
+ // .....
+ // . .
+ // .___.
+ let mut bottom_border_not_white = true;
+ while (bottom_border_not_white || !at_least_one_black_point_found_on_bottom) && down < self.height
+ {
+ bottom_border_not_white = self.contains_black_point(left, right, down, true);
+ if bottom_border_not_white {
+ down += 1;
+ a_black_point_found_on_border = true;
+ at_least_one_black_point_found_on_bottom = true;
+ } else if !at_least_one_black_point_found_on_bottom {
+ down += 1;
+ }
+ }
+
+ if down >= self.height {
+ size_exceeded = true;
+ break;
+ }
+
+ // .....
+ // | .
+ // .....
+ let mut left_border_not_white = true;
+ while (left_border_not_white || !at_least_one_black_point_found_on_left) && left >= 0 {
+ left_border_not_white = self.contains_black_point(up, down, left, false);
+ if left_border_not_white {
+ left -= 1;
+ a_black_point_found_on_border = true;
+ at_least_one_black_point_found_on_left = true;
+ } else if !at_least_one_black_point_found_on_left {
+ left -= 1;
+ }
+ }
+
+ if left < 0 {
+ size_exceeded = true;
+ break;
+ }
+
+ // .___.
+ // . .
+ // .....
+ let mut top_border_not_white = true;
+ while (top_border_not_white || !at_least_one_black_point_found_on_top) && up >= 0 {
+ top_border_not_white = self.contains_black_point(left, right, up, true);
+ if top_border_not_white {
+ up -= 1;
+ a_black_point_found_on_border = true;
+ at_least_one_black_point_found_on_top = true;
+ } else if !at_least_one_black_point_found_on_top {
+ up -= 1;
+ }
+ }
+
+ if up < 0 {
+ size_exceeded = true;
+ break;
+ }
+ }
+
+ if !size_exceeded {
+ let max_size = right - left;
+
+ let mut z: Option = None;
+ let mut i = 1;
+ while z.is_none() && i < max_size {
+ //for (int i = 1; z == null && i < maxSize; i++) {
+ z = self.get_black_point_on_segment(
+ left as f32,
+ (down - i) as f32,
+ (left + i) as f32,
+ down as f32,
+ );
+ i += 1;
+ }
+
+ if z.is_none() {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ let mut t: Option = None;
+ //go down right
+ let mut i = 1;
+ while t.is_none() && i < max_size {
+ //for (int i = 1; t == null && i < maxSize; i++) {
+ t = self.get_black_point_on_segment(
+ left as f32,
+ (up + i) as f32,
+ (left + i) as f32,
+ up as f32,
+ );
+ i += 1;
+ }
+
+ if t.is_none() {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ let mut x: Option = None;
+ //go down left
+ let mut i = 1;
+ while x.is_none() && i < max_size {
+ //for (int i = 1; x == null && i < maxSize; i++) {
+ x = self.get_black_point_on_segment(
+ right as f32,
+ (up + i) as f32,
+ (right - i) as f32,
+ up as f32,
+ );
+ i += 1;
+ }
+
+ if x.is_none() {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ let mut y: Option = None;
+ //go up left
+ let mut i = 1;
+ while y.is_none() && i < max_size {
+ //for (int i = 1; y == null && i < maxSize; i++) {
+ y = self.get_black_point_on_segment(
+ right as f32,
+ (down - i) as f32,
+ (right - i) as f32,
+ down as f32,
+ );
+ i += 1;
+ }
+
+ if y.is_none() {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+
+ return Ok(self.center_edges(&y.unwrap(), &z.unwrap(), &x.unwrap(), &t.unwrap()));
+ } else {
+ return Err(Exceptions::NotFoundException("".to_owned()));
+ }
+ }
+
+ fn get_black_point_on_segment(
+ &self,
+ a_x: f32,
+ a_y: f32,
+ b_x: f32,
+ b_y: f32,
+ ) -> Option {
+ let dist = MathUtils::round(MathUtils::distance_float(a_x, a_y, b_x, b_y));
+ let x_step: f32 = (b_x - a_x) / dist as f32;
+ let y_step: f32 = (b_y - a_y) / dist as f32;
+
+ for i in 0..dist {
+ let x = MathUtils::round(a_x + i as f32 * x_step);
+ let y = MathUtils::round(a_y + i as f32 * y_step);
+ if self.image.get(x as u32, y as u32) {
+ return Some(RXingResultPoint::new(x as f32, y as f32));
+ }
+ }
+ return None;
+ }
+
+ /**
+ * recenters the points of a constant distance towards the center
+ *
+ * @param y bottom most point
+ * @param z left most point
+ * @param x right most point
+ * @param t top most point
+ * @return {@link RXingResultPoint}[] describing the corners of the rectangular
+ * region. The first and last points are opposed on the diagonal, as
+ * are the second and third. The first point will be the topmost
+ * point and the last, the bottommost. The second point will be
+ * leftmost and the third, the rightmost
+ */
+ fn center_edges(
+ &self,
+ y: &RXingResultPoint,
+ z: &RXingResultPoint,
+ x: &RXingResultPoint,
+ t: &RXingResultPoint,
+ ) -> Vec {
+ //
+ // t t
+ // z x
+ // x OR z
+ // y y
+ //
+
+ let yi = y.getX();
+ let yj = y.getY();
+ let zi = z.getX();
+ let zj = z.getY();
+ let xi = x.getX();
+ let xj = x.getY();
+ let ti = t.getX();
+ let tj = t.getY();
+
+ if yi < self.width as f32 / 2.0f32 {
+ return vec![
+ RXingResultPoint::new(ti - CORR as f32, tj + CORR as f32),
+ RXingResultPoint::new(zi + CORR as f32, zj + CORR as f32),
+ RXingResultPoint::new(xi - CORR as f32, xj - CORR as f32),
+ RXingResultPoint::new(yi + CORR as f32, yj - CORR as f32),
+ ];
+ } else {
+ return vec![
+ RXingResultPoint::new(ti + CORR as f32, tj + CORR as f32),
+ RXingResultPoint::new(zi + CORR as f32, zj - CORR as f32),
+ RXingResultPoint::new(xi - CORR as f32, xj + CORR as f32),
+ RXingResultPoint::new(yi - CORR as f32, yj - CORR as f32),
+ ];
+ }
+ }
+
+ /**
+ * Determines whether a segment contains a black point
+ *
+ * @param a min value of the scanned coordinate
+ * @param b max value of the scanned coordinate
+ * @param fixed value of fixed coordinate
+ * @param horizontal set to true if scan must be horizontal, false if vertical
+ * @return true if a black point has been found, else false.
+ */
+ fn contains_black_point(&self, a: i32, b: i32, fixed: i32, horizontal: bool) -> bool {
+ if horizontal {
+ for x in a..=b {
+ if self.image.get(x as u32, fixed as u32) {
+ return true;
+ }
+ }
+ } else {
+ for y in a..=b {
+ if self.image.get(fixed as u32, y as u32) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/common/eci_encoder_set.rs b/src/common/eci_encoder_set.rs
new file mode 100644
index 0000000..3c10cce
--- /dev/null
+++ b/src/common/eci_encoder_set.rs
@@ -0,0 +1,254 @@
+
+/*
+ * Copyright 2021 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.nio.charset.Charset;
+// import java.nio.charset.CharsetEncoder;
+// import java.nio.charset.StandardCharsets;
+// import java.nio.charset.UnsupportedCharsetException;
+// import java.util.ArrayList;
+// import java.util.List;
+
+use encoding::{EncodingRef, Encoding};
+use unicode_segmentation::UnicodeSegmentation;
+
+use super::CharacterSetECI;
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+ static ref ENCODERS : Vec = {
+ let mut enc_vec = Vec::new();
+ for name in NAMES {
+ if let Some(enc) = CharacterSetECI::getCharacterSetECIByName(name) {
+ // try {
+ enc_vec.push(CharacterSetECI::getCharset(&enc));
+ // } catch (UnsupportedCharsetException e) {
+ // continue
+ // }
+ }
+ }
+ enc_vec
+ };
+}
+const NAMES: [&str; 20] = [
+ "IBM437",
+ "ISO-8859-2",
+ "ISO-8859-3",
+ "ISO-8859-4",
+ "ISO-8859-5",
+ "ISO-8859-6",
+ "ISO-8859-7",
+ "ISO-8859-8",
+ "ISO-8859-9",
+ "ISO-8859-10",
+ "ISO-8859-11",
+ "ISO-8859-13",
+ "ISO-8859-14",
+ "ISO-8859-15",
+ "ISO-8859-16",
+ "windows-1250",
+ "windows-1251",
+ "windows-1252",
+ "windows-1256",
+ "Shift_JIS",
+];
+
+/**
+ * Set of CharsetEncoders for a given input string
+ *
+ * Invariants:
+ * - The list contains only encoders from CharacterSetECI (list is shorter then the list of encoders available on
+ * the platform for which ECI values are defined).
+ * - The list contains encoders at least one encoder for every character in the input.
+ * - The first encoder in the list is always the ISO-8859-1 encoder even of no character in the input can be encoded
+ * by it.
+ * - If the input contains a character that is not in ISO-8859-1 then the last two entries in the list will be the
+ * UTF-8 encoder and the UTF-16BE encoder.
+ *
+ * @author Alex Geller
+ */
+#[derive(Clone)]
+pub struct ECIEncoderSet {
+ encoders: Vec,
+ priorityEncoderIndex: Option,
+}
+
+impl ECIEncoderSet {
+ /**
+ * Constructs an encoder set
+ *
+ * @param stringToEncode the string that needs to be encoded
+ * @param priorityCharset The preferred {@link Charset} or null.
+ * @param fnc1 fnc1 denotes the character in the input that represents the FNC1 character or -1 for a non-GS1 bar
+ * code. When specified, it is considered an error to pass it as argument to the methods canEncode() or encode().
+ */
+ pub fn new(
+ stringToEncodeMain: &str,
+ priorityCharset: Option,
+ fnc1: Option<&str>,
+ ) -> Self {
+ // List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
+
+ let mut encoders: Vec;
+ let mut priorityEncoderIndexValue = None;
+
+ let mut neededEncoders: Vec = Vec::new();
+
+ let stringToEncode = stringToEncodeMain.graphemes(true).collect::>();
+
+ //we always need the ISO-8859-1 encoder. It is the default encoding
+ neededEncoders.push(encoding::all::ISO_8859_1);
+ let mut needUnicodeEncoder = if let Some(pc) = priorityCharset {
+ pc.name().starts_with("UTF")
+ } else {
+ false
+ };
+
+ //Walk over the input string and see if all characters can be encoded with the list of encoders
+ for i in 0..stringToEncode.len() {
+ // for (int i = 0; i < stringToEncode.length(); i++) {
+ let mut canEncode = false;
+ for encoder in &neededEncoders {
+ // for (CharsetEncoder encoder : neededEncoders) {
+ let c = stringToEncode.get(i).unwrap();
+ if (fnc1.is_some() && c == fnc1.as_ref().unwrap())
+ || encoder.encode(c, encoding::EncoderTrap::Strict).is_ok()
+ {
+ canEncode = true;
+ break;
+ }
+ }
+ if !canEncode {
+ //for the character at position i we don't yet have an encoder in the list
+ for i_encoder in 0..ENCODERS.len() {
+ // for encoder in ENCODERS {
+ let encoder = ENCODERS.get(i_encoder).unwrap();
+ // for (CharsetEncoder encoder : ENCODERS) {
+ if encoder
+ .encode(
+ &stringToEncode.get(i).unwrap(),
+ encoding::EncoderTrap::Strict,
+ )
+ .is_ok()
+ {
+ //Good, we found an encoder that can encode the character. We add him to the list and continue scanning
+ //the input
+ neededEncoders.push(*encoder);
+ canEncode = true;
+ break;
+ }
+ }
+ }
+
+ if !canEncode {
+ //The character is not encodeable by any of the single byte encoders so we remember that we will need a
+ //Unicode encoder.
+ needUnicodeEncoder = true;
+ }
+ }
+
+ if neededEncoders.len() == 1 && !needUnicodeEncoder {
+ //the entire input can be encoded by the ISO-8859-1 encoder
+ encoders = vec![encoding::all::ISO_8859_1];
+ } else {
+ // we need more than one single byte encoder or we need a Unicode encoder.
+ // In this case we append a UTF-8 and UTF-16 encoder to the list
+ // encoders = [] new CharsetEncoder[neededEncoders.size() + 2];
+ encoders = Vec::new();
+ let index = 0;
+
+ for encoder in neededEncoders {
+ // for (CharsetEncoder encoder : neededEncoders) {
+ //encoders[index++] = encoder;
+ encoders.push(encoder);
+ }
+
+ encoders.push(encoding::all::UTF_8);
+ encoders.push(encoding::all::UTF_16BE);
+ }
+
+ //Compute priorityEncoderIndex by looking up priorityCharset in encoders
+ // if priorityCharset != null {
+ if priorityCharset.is_some() {
+ for i in 0..encoders.len() {
+ // for (int i = 0; i < encoders.length; i++) {
+ if priorityCharset.as_ref().unwrap().name() == encoders[i].name() {
+ priorityEncoderIndexValue = Some(i);
+ break;
+ }
+ }
+ }
+ // }
+ //invariants
+ assert_eq!(encoders[0].name(), encoding::all::ISO_8859_1.name());
+ Self {
+ encoders: encoders,
+ priorityEncoderIndex: priorityEncoderIndexValue,
+ }
+ }
+
+ pub fn len(&self) -> usize {
+ return self.encoders.len();
+ }
+
+ pub fn getCharsetName(&self, index: usize) -> &'static str {
+ assert!(index < self.len());
+ return self.encoders[index].name();
+ }
+
+ pub fn getCharset(&self, index: usize) -> EncodingRef {
+ assert!(index < self.len());
+ return self.encoders[index];
+ }
+
+ pub fn getECIValue(&self, encoderIndex: usize) -> u32 {
+ CharacterSetECI::getValue(
+ &CharacterSetECI::getCharacterSetECI(self.encoders[encoderIndex]).unwrap(),
+ )
+ }
+
+ /*
+ * returns -1 if no priority charset was defined
+ */
+ pub fn getPriorityEncoderIndex(&self) -> Option {
+ self.priorityEncoderIndex
+ }
+
+ pub fn canEncode(&self, c: &str, encoderIndex: usize) -> bool {
+ assert!(encoderIndex < self.len());
+ let encoder = self.encoders[encoderIndex];
+ let enc_data = encoder.encode(c, encoding::EncoderTrap::Strict);
+
+ enc_data.is_ok()
+ }
+
+ pub fn encode_char(&self, c: &str, encoderIndex: usize) -> Vec {
+ assert!(encoderIndex < self.len());
+ let encoder = self.encoders[encoderIndex];
+ let enc_data = encoder.encode(&c.to_string(), encoding::EncoderTrap::Strict);
+ assert!(enc_data.is_ok());
+ return enc_data.unwrap();
+ }
+
+ pub fn encode_string(&self, s: &str, encoderIndex: usize) -> Vec {
+ assert!(encoderIndex < self.len());
+ let encoder = self.encoders[encoderIndex];
+ encoder.encode(s, encoding::EncoderTrap::Replace).unwrap()
+ }
+}
\ No newline at end of file
diff --git a/src/common/eci_input.rs b/src/common/eci_input.rs
new file mode 100644
index 0000000..b84db68
--- /dev/null
+++ b/src/common/eci_input.rs
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright 2021 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;
+
+use crate::Exceptions;
+
+/**
+ * Interface to navigate a sequence of ECIs and bytes.
+ *
+ * @author Alex Geller
+ */
+pub trait ECIInput {
+ /**
+ * Returns the length of this input. The length is the number
+ * of {@code byte}s in or ECIs in the sequence.
+ *
+ * @return the number of {@code char}s in this sequence
+ */
+ fn length(&self) -> usize;
+
+ /**
+ * Returns the {@code byte} value at the specified index. An index ranges from zero
+ * to {@code length() - 1}. The first {@code byte} value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing.
+ *
+ * @param index the index of the {@code byte} value to be returned
+ *
+ * @return the specified {@code byte} value as character or the FNC1 character
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ * @throws IllegalArgumentException
+ * if the value at the {@code index} argument is an ECI (@see #isECI)
+ */
+ fn charAt(&self, index: usize) -> Result;
+
+ /**
+ * Returns a {@code CharSequence} that is a subsequence of this sequence.
+ * The subsequence starts with the {@code char} value at the specified index and
+ * ends with the {@code char} value at index {@code end - 1}. The length
+ * (in {@code char}s) of the
+ * returned sequence is {@code end - start}, so if {@code start == end}
+ * then an empty sequence is returned.
+ *
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ *
+ * @return the specified subsequence
+ *
+ * @throws IndexOutOfBoundsException
+ * if {@code start} or {@code end} are negative,
+ * if {@code end} is greater than {@code length()},
+ * or if {@code start} is greater than {@code end}
+ * @throws IllegalArgumentException
+ * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI)
+ */
+ fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions>;
+
+ /**
+ * Determines if a value is an ECI
+ *
+ * @param index the index of the value
+ *
+ * @return true if the value at position {@code index} is an ECI
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ */
+ fn isECI(&self, index: u32) -> Result;
+
+ /**
+ * Returns the {@code int} ECI value at the specified index. An index ranges from zero
+ * to {@code length() - 1}. The first {@code byte} value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing.
+ *
+ * @param index the index of the {@code int} value to be returned
+ *
+ * @return the specified {@code int} ECI value.
+ * The ECI specified the encoding of all bytes with a higher index until the
+ * next ECI or until the end of the input if no other ECI follows.
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ * @throws IllegalArgumentException
+ * if the value at the {@code index} argument is not an ECI (@see #isECI)
+ */
+ fn getECIValue(&self, index: usize) -> Result;
+ fn haveNCharacters(&self, index: usize, n: usize) -> bool;
+}
\ No newline at end of file
diff --git a/src/common/eci_string_builder.rs b/src/common/eci_string_builder.rs
new file mode 100644
index 0000000..0b78fde
--- /dev/null
+++ b/src/common/eci_string_builder.rs
@@ -0,0 +1,173 @@
+
+/*
+ * Copyright 2022 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 com.google.zxing.FormatException;
+
+// import java.nio.charset.Charset;
+// import java.nio.charset.StandardCharsets;
+
+use std::fmt;
+
+use encoding::{EncodingRef, Encoding};
+
+use crate::Exceptions;
+
+use super::CharacterSetECI;
+
+/**
+ * Class that converts a sequence of ECIs and bytes into a string
+ *
+ * @author Alex Geller
+ */
+pub struct ECIStringBuilder {
+ current_bytes: Vec,
+ result: String,
+ current_charset: EncodingRef, //= StandardCharsets.ISO_8859_1;
+}
+
+impl ECIStringBuilder {
+ pub fn new() -> Self {
+ Self {
+ current_bytes: Vec::new(),
+ result: String::new(),
+ current_charset: encoding::all::UTF_8,
+ }
+ }
+ pub fn with_capacity(initial_capacity: usize) -> Self {
+ Self {
+ current_bytes: Vec::with_capacity(initial_capacity),
+ result: String::new(),
+ current_charset: encoding::all::ISO_8859_1,
+ }
+ }
+
+ /**
+ * Appends {@code value} as a byte value
+ *
+ * @param value character whose lowest byte is to be appended
+ */
+ pub fn append_char(&mut self, value: char) {
+ self.current_bytes.push(value as u8);
+ }
+
+ /**
+ * Appends {@code value} as a byte value
+ *
+ * @param value byte to append
+ */
+ pub fn append_byte(&mut self, value: u8) {
+ self.current_bytes.push(value);
+ }
+
+ /**
+ * Appends the characters in {@code value} as bytes values
+ *
+ * @param value string to append
+ */
+ pub fn append_string(&mut self, value: &str) {
+ value.as_bytes().iter().map(|b| self.current_bytes.push(*b));
+ // self.current_bytes.push(value.as_bytes());
+ }
+
+ /**
+ * Append the string repesentation of {@code value} (short for {@code append(String.valueOf(value))})
+ *
+ * @param value int to append as a string
+ */
+ pub fn append(&mut self, value: i32) {
+ self.append_string(&format!("{}", value));
+ }
+
+ /**
+ * Appends ECI value to output.
+ *
+ * @param value ECI value to append, as an int
+ * @throws FormatException on invalid ECI value
+ */
+ pub fn appendECI(&mut self, value: u32) -> Result<(), Exceptions> {
+ self.encodeCurrentBytesIfAny();
+ let character_set_eci = CharacterSetECI::getCharacterSetECIByValue(value)?;
+ // if (character_set_eci == null) {
+ // throw FormatException.getFormatInstance();
+ // }
+ self.current_charset = CharacterSetECI::getCharset(&character_set_eci);
+ Ok(())
+ }
+
+ pub fn encodeCurrentBytesIfAny(&mut self) {
+ if self.current_charset.name() == encoding::all::UTF_8.name() {
+ if !self.current_bytes.is_empty() {
+ // if result == null {
+ // result = currentBytes;
+ // currentBytes = new StringBuilder();
+ // } else {
+ self.result
+ .push_str(&String::from_utf8(self.current_bytes.clone()).unwrap());
+ self.current_bytes.clear();
+ // }
+ }
+ } else if !self.current_bytes.is_empty() {
+ let bytes = self.current_bytes.clone();
+ self.current_bytes.clear();
+ // if (result == null) {
+ // result = new StringBuilder(new String(bytes, currentCharset));
+ // } else {
+ let encoded_value = self
+ .current_charset
+ .decode(&bytes, encoding::DecoderTrap::Replace)
+ .unwrap();
+ self.result.push_str(&encoded_value);
+ // }
+ }
+ }
+
+ /**
+ * Appends the characters from {@code value} (unlike all other append methods of this class who append bytes)
+ *
+ * @param value characters to append
+ */
+ pub fn appendCharacters(&mut self, value: &str) {
+ self.encodeCurrentBytesIfAny();
+ self.result.push_str(value);
+ }
+
+ /**
+ * Short for {@code toString().length()} (if possible, use {@link #isEmpty()} instead)
+ *
+ * @return length of string representation in characters
+ */
+ pub fn len(&mut self) -> usize {
+ self.encodeCurrentBytesIfAny(); //return toString().length();
+ self.result.len()
+ }
+
+ /**
+ * @return true iff nothing has been appended
+ */
+ pub fn is_empty(&self) -> bool {
+ return self.current_bytes.is_empty() && self.result.is_empty();
+ }
+}
+
+impl fmt::Display for ECIStringBuilder {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ //self.encodeCurrentBytesIfAny();
+ write!(f, "{}", self.result)
+ }
+}
\ No newline at end of file
diff --git a/src/common/global_histogram_binarizer.rs b/src/common/global_histogram_binarizer.rs
new file mode 100644
index 0000000..415b5b3
--- /dev/null
+++ b/src/common/global_histogram_binarizer.rs
@@ -0,0 +1,247 @@
+
+/*
+ * Copyright 2009 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 com.google.zxing.Binarizer;
+// import com.google.zxing.LuminanceSource;
+// import com.google.zxing.NotFoundException;
+
+use crate::{Exceptions, LuminanceSource, Binarizer};
+
+use super::{BitMatrix, BitArray};
+
+/**
+ * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
+ * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
+ * algorithm. However, because it picks a global black point, it cannot handle difficult shadows
+ * and gradients.
+ *
+ * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+pub struct GlobalHistogramBinarizer {
+ luminances: Vec,
+ buckets: Vec,
+ width: usize,
+ height: usize,
+ source: Box,
+}
+
+impl Binarizer for GlobalHistogramBinarizer {
+ fn getLuminanceSource(&self) -> &Box {
+ &self.source
+ }
+
+ // Applies simple sharpening to the row data to improve performance of the 1D Readers.
+ fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result {
+ let source = self.getLuminanceSource();
+ let width = source.getWidth();
+ let mut row = if row.getSize() < width {
+ BitArray::with_size(width)
+ } else {
+ let mut z = row.clone();
+ z.clear();
+ z
+ };
+
+ // self.initArrays(width);
+ let localLuminances = source.getRow(y, &self.luminances);
+ let mut localBuckets = self.buckets.clone();
+ for x in 0..width {
+ // for (int x = 0; x < width; x++) {
+ localBuckets
+ [((localLuminances[x]) >> GlobalHistogramBinarizer::LUMINANCE_SHIFT) as usize] += 1;
+ }
+ let blackPoint = self.estimateBlackPoint(&localBuckets)?;
+
+ if width < 3 {
+ // Special case for very small images
+ for x in 0..width {
+ // for (int x = 0; x < width; x++) {
+ if (localLuminances[x] as u32) < blackPoint {
+ row.set(x);
+ }
+ }
+ } else {
+ let mut left = localLuminances[0]; // & 0xff;
+ let mut center = localLuminances[1]; // & 0xff;
+ for x in 1..width - 1 {
+ // for (int x = 1; x < width - 1; x++) {
+ let right = localLuminances[x + 1] & 0xff;
+ // A simple -1 4 -1 box filter with a weight of 2.
+ if ((center * 4) - left - right) as u32 / 2 < blackPoint {
+ row.set(x);
+ }
+ left = center;
+ center = right;
+ }
+ }
+ Ok(row)
+ }
+
+ // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
+ fn getBlackMatrix(&self) -> Result {
+ let source = self.getLuminanceSource();
+ let width = source.getWidth();
+ let height = source.getHeight();
+ let mut matrix = BitMatrix::new(width as u32, height as u32)?;
+
+ // Quickly calculates the histogram by sampling four rows from the image. This proved to be
+ // more robust on the blackbox tests than sampling a diagonal as we used to do.
+ // self.initArrays(width);
+ let mut localBuckets = self.buckets.clone();
+ for y in 1..5 {
+ // for (int y = 1; y < 5; y++) {
+ let row = height * y / 5;
+ let localLuminances = source.getRow(row, &self.luminances);
+ let right = (width * 4) / 5;
+ let mut x = width / 5;
+ while x < right {
+ // for (int x = width / 5; x < right; x++) {
+ let pixel = localLuminances[x];
+ localBuckets[(pixel >> GlobalHistogramBinarizer::LUMINANCE_SHIFT) as usize] += 1;
+ x += 1;
+ }
+ }
+ let blackPoint = self.estimateBlackPoint(&localBuckets)?;
+
+ // We delay reading the entire image luminance until the black point estimation succeeds.
+ // Although we end up reading four rows twice, it is consistent with our motto of
+ // "fail quickly" which is necessary for continuous scanning.
+ let localLuminances = source.getMatrix();
+ for y in 0..height {
+ // for (int y = 0; y < height; y++) {
+ let offset = y * width;
+ for x in 0..width {
+ // for (int x = 0; x < width; x++) {
+ let pixel = localLuminances[offset + x] & 0xff;
+ if (pixel as u32) < blackPoint {
+ matrix.set(x as u32, y as u32);
+ }
+ }
+ }
+
+ Ok(matrix)
+ }
+
+ fn createBinarizer(&self, source: Box) -> Box {
+ return Box::new(GlobalHistogramBinarizer::new(source));
+ }
+
+ fn getWidth(&self) -> usize {
+ self.width
+ }
+
+ fn getHeight(&self) -> usize {
+ self.height
+ }
+}
+
+impl GlobalHistogramBinarizer {
+ const LUMINANCE_BITS: usize = 5;
+ const LUMINANCE_SHIFT: usize = 8 - GlobalHistogramBinarizer::LUMINANCE_BITS;
+ const LUMINANCE_BUCKETS: usize = 1 << GlobalHistogramBinarizer::LUMINANCE_BITS;
+ const EMPTY: [u8; 0] = [0; 0];
+
+ pub fn new(source: Box) -> Self {
+ Self {
+ luminances: vec![0; source.getWidth()],
+ buckets: vec![0; GlobalHistogramBinarizer::LUMINANCE_BUCKETS],
+ width: source.getWidth(),
+ height: source.getHeight(),
+ source: source,
+ }
+ }
+
+ // fn initArrays(&mut self, luminanceSize: usize) {
+ // // if self.luminances.len() < luminanceSize {
+ // // self.luminances = ;
+ // // }
+ // // // for x in 0..GlobalHistogramBinarizer::LUMINANCE_BUCKETS {
+ // // // for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
+ // // self.buckets[x] = 0;
+ // // }
+ // }
+
+ fn estimateBlackPoint(&self, buckets: &[u32]) -> Result {
+ // Find the tallest peak in the histogram.
+ let numBuckets = buckets.len();
+ let mut maxBucketCount = 0;
+ let mut firstPeak = 0;
+ let mut firstPeakSize = 0;
+ for x in 0..numBuckets {
+ // for (int x = 0; x < numBuckets; x++) {
+ if buckets[x] > firstPeakSize {
+ firstPeak = x;
+ firstPeakSize = buckets[x];
+ }
+ if buckets[x] > maxBucketCount {
+ maxBucketCount = buckets[x];
+ }
+ }
+
+ // Find the second-tallest peak which is somewhat far from the tallest peak.
+ let mut secondPeak = 0;
+ let mut secondPeakScore = 0;
+ for x in 0..numBuckets {
+ // for (int x = 0; x < numBuckets; x++) {
+ let distanceToBiggest = x - firstPeak;
+ // Encourage more distant second peaks by multiplying by square of distance.
+ let score = buckets[x] * distanceToBiggest as u32 * distanceToBiggest as u32;
+ if score > secondPeakScore {
+ secondPeak = x;
+ secondPeakScore = score;
+ }
+ }
+
+ // Make sure firstPeak corresponds to the black peak.
+ if firstPeak > secondPeak {
+ let temp = firstPeak;
+ firstPeak = secondPeak;
+ secondPeak = temp;
+ }
+
+ // If there is too little contrast in the image to pick a meaningful black point, throw rather
+ // than waste time trying to decode the image, and risk false positives.
+ if secondPeak - firstPeak <= numBuckets / 16 {
+ return Err(Exceptions::NotFoundException(
+ "secondPeak - firstPeak <= numBuckets / 16 ".to_owned(),
+ ));
+ }
+
+ // Find a valley between them that is low and closer to the white peak.
+ let mut bestValley = secondPeak - 1;
+ let mut bestValleyScore = -1i32;
+ let mut x = secondPeak;
+ while x > firstPeak {
+ // for (int x = secondPeak - 1; x > firstPeak; x--) {
+ let fromFirst = x - firstPeak;
+ let score =
+ fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]) as usize;
+ if score as i32 > bestValleyScore {
+ bestValley = x;
+ bestValleyScore = score as i32;
+ }
+ x -= 1;
+ }
+
+ Ok((bestValley as u32) << GlobalHistogramBinarizer::LUMINANCE_SHIFT)
+ }
+}
\ No newline at end of file
diff --git a/src/common/grid_sampler.rs b/src/common/grid_sampler.rs
new file mode 100644
index 0000000..b535a11
--- /dev/null
+++ b/src/common/grid_sampler.rs
@@ -0,0 +1,200 @@
+
+/*
+ * 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 com.google.zxing.NotFoundException;
+
+use crate::Exceptions;
+
+use super::{BitMatrix, PerspectiveTransform};
+
+/**
+ * Implementations of this class can, given locations of finder patterns for a QR code in an
+ * image, sample the right points in the image to reconstruct the QR code, accounting for
+ * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
+ * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
+ * Imaging library, but which may not be available in other environments such as J2ME, and vice
+ * versa.
+ *
+ * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
+ * with an instance of a class which implements this interface.
+ *
+ * @author Sean Owen
+ */
+
+pub trait GridSampler {
+ // /**
+ // * Sets the implementation of GridSampler used by the library. One global
+ // * instance is stored, which may sound problematic. But, the implementation provided
+ // * ought to be appropriate for the entire platform, and all uses of this library
+ // * in the whole lifetime of the JVM. For instance, an Android activity can swap in
+ // * an implementation that takes advantage of native platform libraries.
+ // *
+ // * @param newGridSampler The platform-specific object to install.
+ // */
+ // public static void setGridSampler(GridSampler newGridSampler) {
+ // gridSampler = newGridSampler;
+ // }
+
+ // /**
+ // * @return the current implementation of GridSampler
+ // */
+ // public static GridSampler getInstance() {
+ // return gridSampler;
+ // }
+
+ /**
+ * Samples an image for a rectangular matrix of bits of the given dimension. The sampling
+ * transformation is determined by the coordinates of 4 points, in the original and transformed
+ * image space.
+ *
+ * @param image image to sample
+ * @param dimensionX width of {@link BitMatrix} to sample from image
+ * @param dimensionY height of {@link BitMatrix} to sample from image
+ * @param p1ToX point 1 preimage X
+ * @param p1ToY point 1 preimage Y
+ * @param p2ToX point 2 preimage X
+ * @param p2ToY point 2 preimage Y
+ * @param p3ToX point 3 preimage X
+ * @param p3ToY point 3 preimage Y
+ * @param p4ToX point 4 preimage X
+ * @param p4ToY point 4 preimage Y
+ * @param p1FromX point 1 image X
+ * @param p1FromY point 1 image Y
+ * @param p2FromX point 2 image X
+ * @param p2FromY point 2 image Y
+ * @param p3FromX point 3 image X
+ * @param p3FromY point 3 image Y
+ * @param p4FromX point 4 image X
+ * @param p4FromY point 4 image Y
+ * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
+ * defined by the "from" parameters
+ * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
+ * by the given points is invalid or results in sampling outside the image boundaries
+ */
+ fn sample_grid_detailed(
+ &self,
+ image: &BitMatrix,
+ dimensionX: u32,
+ dimensionY: u32,
+ p1ToX: f32,
+ p1ToY: f32,
+ p2ToX: f32,
+ p2ToY: f32,
+ p3ToX: f32,
+ p3ToY: f32,
+ p4ToX: f32,
+ p4ToY: f32,
+ p1FromX: f32,
+ p1FromY: f32,
+ p2FromX: f32,
+ p2FromY: f32,
+ p3FromX: f32,
+ p3FromY: f32,
+ p4FromX: f32,
+ p4FromY: f32,
+ ) -> Result;
+
+ fn sample_grid(
+ &self,
+ image: &BitMatrix,
+ dimensionX: u32,
+ dimensionY: u32,
+ transform: &PerspectiveTransform,
+ ) -> Result;
+
+ /**
+ * Checks a set of points that have been transformed to sample points on an image against
+ * the image's dimensions to see if the point are even within the image.
+ *
+ * This method will actually "nudge" the endpoints back onto the image if they are found to be
+ * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
+ * patterns in an image where the QR Code runs all the way to the image border.
+ *
+ * For efficiency, the method will check points from either end of the line until one is found
+ * to be within the image. Because the set of points are assumed to be linear, this is valid.
+ *
+ * @param image image into which the points should map
+ * @param points actual points in x1,y1,...,xn,yn form
+ * @throws NotFoundException if an endpoint is lies outside the image boundaries
+ */
+ fn checkAndNudgePoints(&self, image: &BitMatrix, points: &mut [f32]) -> Result<(), Exceptions> {
+ let width = image.getWidth();
+ let height = image.getHeight();
+ // Check and nudge points from start until we see some that are OK:
+ let mut nudged = true;
+ let max_offset = points.len() - 1; // points.length must be even
+ let mut offset = 0;
+ while offset < max_offset && nudged {
+ // for (int offset = 0; offset < maxOffset && nudged; offset += 2) {
+ let x = points[offset] as i32;
+ let y = points[offset + 1] as i32;
+ if x < -1 || x > width.try_into().unwrap() || y < -1 || y > height.try_into().unwrap() {
+ return Err(Exceptions::NotFoundException(
+ "getNotFoundInstance".to_owned(),
+ ));
+ }
+ nudged = false;
+ if x == -1 {
+ points[offset] = 0.0f32;
+ nudged = true;
+ } else if x == width.try_into().unwrap() {
+ points[offset] = width as f32 - 1f32;
+ nudged = true;
+ }
+ if y == -1 {
+ points[offset + 1] = 0.0f32;
+ nudged = true;
+ } else if (y == height.try_into().unwrap()) {
+ points[offset + 1] = height as f32 - 1f32;
+ nudged = true;
+ }
+ offset += 2;
+ }
+ // Check and nudge points from end:
+ nudged = true;
+ let mut offset = points.len() as isize - 2;
+ while offset >= 0 && nudged {
+ // for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
+ let x = points[offset as usize] as i32;
+ let y = points[offset as usize + 1] as i32;
+ if x < -1 || x > width.try_into().unwrap() || y < -1 || y > height.try_into().unwrap() {
+ return Err(Exceptions::NotFoundException(
+ "getNotFoundInstance".to_owned(),
+ ));
+ }
+ nudged = false;
+ if x == -1 {
+ points[offset as usize] = 0.0f32;
+ nudged = true;
+ } else if x == width.try_into().unwrap() {
+ points[offset as usize] = width as f32 - 1f32;
+ nudged = true;
+ }
+ if y == -1 {
+ points[offset as usize + 1] = 0.0f32;
+ nudged = true;
+ } else if y == height.try_into().unwrap() {
+ points[offset as usize + 1] = height as f32 - 1f32;
+ nudged = true;
+ }
+ offset += -2;
+ }
+ Ok(())
+ }
+}
\ No newline at end of file
diff --git a/src/common/hybrid_binarizer.rs b/src/common/hybrid_binarizer.rs
new file mode 100644
index 0000000..62cfb0b
--- /dev/null
+++ b/src/common/hybrid_binarizer.rs
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2009 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 com.google.zxing.Binarizer;
+// import com.google.zxing.LuminanceSource;
+// import com.google.zxing.NotFoundException;
+
+use crate::{LuminanceSource, Binarizer, Exceptions};
+
+use super::{GlobalHistogramBinarizer, BitMatrix, BitArray};
+
+/**
+ * This class implements a local thresholding algorithm, which while slower than the
+ * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
+ * high frequency images of barcodes with black data on white backgrounds. For this application,
+ * it does a much better job than a global blackpoint with severe shadows and gradients.
+ * However it tends to produce artifacts on lower frequency images and is therefore not
+ * a good general purpose binarizer for uses outside ZXing.
+ *
+ * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
+ * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
+ * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
+ * but for now it was not a win to use local blocks for 1D.
+ *
+ * This Binarizer is the default for the unit tests and the recommended class for library users.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+pub struct HybridBinarizer {
+ //width: usize,
+ //height: usize,
+ //source: Box,
+ ghb: GlobalHistogramBinarizer,
+ // matrix :Option,
+}
+impl Binarizer for HybridBinarizer {
+ fn getLuminanceSource(&self) -> &Box {
+ self.ghb.getLuminanceSource()
+ }
+
+ fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result {
+ self.ghb.getBlackRow(y, row)
+ }
+
+ /**
+ * Calculates the final BitMatrix once for all requests. This could be called once from the
+ * constructor instead, but there are some advantages to doing it lazily, such as making
+ * profiling easier, and not doing heavy lifting when callers don't expect it.
+ */
+ fn getBlackMatrix(&self) -> Result {
+ // if self.matrix.is_some() {
+ // return Ok(self.matrix.clone().unwrap())
+ // }
+ let matrix;
+ let source = self.getLuminanceSource();
+ let width = source.getWidth();
+ let height = source.getHeight();
+ if width >= HybridBinarizer::MINIMUM_DIMENSION
+ && height >= HybridBinarizer::MINIMUM_DIMENSION
+ {
+ let luminances = source.getMatrix();
+ let mut sub_width = width >> HybridBinarizer::BLOCK_SIZE_POWER;
+ if (width & HybridBinarizer::BLOCK_SIZE_MASK) != 0 {
+ sub_width += 1;
+ }
+ let mut sub_height = height >> HybridBinarizer::BLOCK_SIZE_POWER;
+ if (height & HybridBinarizer::BLOCK_SIZE_MASK) != 0 {
+ sub_height += 1;
+ }
+ let black_points = Self::calculateBlackPoints(
+ &luminances,
+ sub_width as u32,
+ sub_height as u32,
+ width as u32,
+ height as u32,
+ );
+
+ let mut new_matrix = BitMatrix::new(width as u32, height as u32)?;
+ Self::calculateThresholdForBlock(
+ &luminances,
+ sub_width as u32,
+ sub_height as u32,
+ width as u32,
+ height as u32,
+ &black_points,
+ &mut new_matrix,
+ );
+ matrix = new_matrix;
+ } else {
+ // If the image is too small, fall back to the global histogram approach.
+ matrix = self.ghb.getBlackMatrix()?;
+ }
+ // dbg!(matrix.to_string());
+ Ok(matrix)
+ }
+
+ fn createBinarizer(&self, source: Box) -> Box {
+ Box::new(HybridBinarizer::new(source))
+ }
+
+ fn getWidth(&self) -> usize {
+ self.ghb.getWidth()
+ }
+
+ fn getHeight(&self) -> usize {
+ self.ghb.getHeight()
+ }
+}
+impl HybridBinarizer {
+ // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
+ // So this is the smallest dimension in each axis we can accept.
+ const BLOCK_SIZE_POWER: usize = 3;
+ const BLOCK_SIZE: usize = 1 << HybridBinarizer::BLOCK_SIZE_POWER; // ...0100...00
+ const BLOCK_SIZE_MASK: usize = HybridBinarizer::BLOCK_SIZE - 1; // ...0011...11
+ const MINIMUM_DIMENSION: usize = HybridBinarizer::BLOCK_SIZE * 5;
+ const MIN_DYNAMIC_RANGE: usize = 24;
+
+ pub fn new(source: Box) -> Self {
+ Self {
+ ghb: GlobalHistogramBinarizer::new(source),
+ // matrix: None,
+ }
+ }
+
+ /**
+ * For each block in the image, calculate the average black point using a 5x5 grid
+ * of the blocks around it. Also handles the corner cases (fractional blocks are computed based
+ * on the last pixels in the row/column which are also used in the previous block).
+ */
+ fn calculateThresholdForBlock(
+ luminances: &[u8],
+ sub_width: u32,
+ sub_height: u32,
+ width: u32,
+ height: u32,
+ black_points: &Vec>,
+ matrix: &mut BitMatrix,
+ ) {
+ let maxYOffset = height - HybridBinarizer::BLOCK_SIZE as u32;
+ let maxXOffset = width - HybridBinarizer::BLOCK_SIZE as u32;
+ for y in 0..sub_height {
+ // for (int y = 0; y < subHeight; y++) {
+ let mut yoffset = y << HybridBinarizer::BLOCK_SIZE_POWER;
+ if yoffset > maxYOffset {
+ yoffset = maxYOffset;
+ }
+ let top = Self::cap(y, sub_height - 3);
+ for x in 0..sub_width {
+ // for (int x = 0; x < subWidth; x++) {
+ let mut xoffset = x << HybridBinarizer::BLOCK_SIZE_POWER;
+ if xoffset > maxXOffset {
+ xoffset = maxXOffset;
+ }
+ let left = Self::cap(x, sub_width - 3);
+ let mut sum = 0;
+ for z in -2i32..=2 {
+ // for (int z = -2; z <= 2; z++) {
+ let blackRow = &black_points[(top as i32 + z) as usize];
+ sum += blackRow[(left - 2) as usize]
+ + blackRow[(left - 1) as usize]
+ + blackRow[left as usize]
+ + blackRow[(left + 1) as usize]
+ + blackRow[(left + 2) as usize];
+ }
+ let average = sum / 25;
+ Self::thresholdBlock(luminances, xoffset, yoffset, average, width, matrix);
+ }
+ }
+ }
+
+ fn cap(value: u32, max: u32) -> u32 {
+ if value < 2 {
+ 2
+ } else {
+ value.min(max)
+ }
+ }
+
+ /**
+ * Applies a single threshold to a block of pixels.
+ */
+ fn thresholdBlock(
+ luminances: &[u8],
+ xoffset: u32,
+ yoffset: u32,
+ threshold: u32,
+ stride: u32,
+ matrix: &mut BitMatrix,
+ ) {
+ let mut offset = yoffset * stride + xoffset;
+ for y in 0..HybridBinarizer::BLOCK_SIZE {
+ // for (int y = 0, offset = yoffset * stride + xoffset; y < HybridBinarizer::BLOCK_SIZE; y++, offset += stride) {
+ for x in 0..HybridBinarizer::BLOCK_SIZE {
+ // for (int x = 0; x < HybridBinarizer::BLOCK_SIZE; x++) {
+ // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
+ if luminances[offset as usize + x] as u32 <= threshold {
+ matrix.set(xoffset + x as u32, yoffset + y as u32);
+ }
+ }
+ offset += stride;
+ }
+ }
+
+ /**
+ * Calculates a single black point for each block of pixels and saves it away.
+ * See the following thread for a discussion of this algorithm:
+ * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
+ */
+ fn calculateBlackPoints(
+ luminances: &[u8],
+ subWidth: u32,
+ subHeight: u32,
+ width: u32,
+ height: u32,
+ ) -> Vec> {
+ let maxYOffset = height as usize - HybridBinarizer::BLOCK_SIZE;
+ let maxXOffset = width as usize - HybridBinarizer::BLOCK_SIZE;
+ let mut blackPoints = vec![vec![0; subWidth as usize]; subHeight as usize];
+ for y in 0..subHeight {
+ // for (int y = 0; y < subHeight; y++) {
+ let mut yoffset = y << HybridBinarizer::BLOCK_SIZE_POWER;
+ if yoffset > maxYOffset as u32 {
+ yoffset = maxYOffset as u32;
+ }
+ for x in 0..subWidth {
+ // for (int x = 0; x < subWidth; x++) {
+ let mut xoffset = x << HybridBinarizer::BLOCK_SIZE_POWER;
+ if xoffset > maxXOffset as u32 {
+ xoffset = maxXOffset as u32;
+ }
+ let mut sum = 0u32;
+ let mut min = 0xff;
+ let mut max = 0;
+
+ let mut offset = yoffset * width + xoffset;
+ let mut yy = 0;
+ while yy < HybridBinarizer::BLOCK_SIZE {
+ // for (int yy = 0, offset = yoffset * width + xoffset; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) {
+ for xx in 0..HybridBinarizer::BLOCK_SIZE {
+ // for (int xx = 0; xx < HybridBinarizer::BLOCK_SIZE; xx++) {
+ let pixel = luminances[offset as usize + xx];
+ sum += pixel as u32;
+ // still looking for good contrast
+ if pixel < min {
+ min = pixel;
+ }
+ if pixel > max {
+ max = pixel;
+ }
+ }
+ // short-circuit min/max tests once dynamic range is met
+ if (max - min) as usize > HybridBinarizer::MIN_DYNAMIC_RANGE {
+ // finish the rest of the rows quickly
+ offset += width;
+ yy += 1;
+ while yy < HybridBinarizer::BLOCK_SIZE {
+ // for (yy++, offset += width; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) {
+ for xx in 0..HybridBinarizer::BLOCK_SIZE {
+ // for (int xx = 0; xx < BLOCK_SIZE; xx++) {
+ sum += luminances[offset as usize + xx] as u32;
+ }
+ yy += 1;
+ offset += width;
+ }
+ break;
+ }
+ yy += 1;
+ offset += width;
+ }
+
+ // The default estimate is the average of the values in the block.
+ let mut average = sum >> (HybridBinarizer::BLOCK_SIZE_POWER * 2);
+ if (max - min) as usize <= HybridBinarizer::MIN_DYNAMIC_RANGE {
+ // If variation within the block is low, assume this is a block with only light or only
+ // dark pixels. In that case we do not want to use the average, as it would divide this
+ // low contrast area into black and white pixels, essentially creating data out of noise.
+ //
+ // The default assumption is that the block is light/background. Since no estimate for
+ // the level of dark pixels exists locally, use half the min for the block.
+ average = min as u32 / 2;
+
+ if y > 0 && x > 0 {
+ // Correct the "white background" assumption for blocks that have neighbors by comparing
+ // the pixels in this block to the previously calculated black points. This is based on
+ // the fact that dark barcode symbology is always surrounded by some amount of light
+ // background for which reasonable black point estimates were made. The bp estimated at
+ // the boundaries is used for the interior.
+
+ // The (min < bp) is arbitrary but works better than other heuristics that were tried.
+ let average_neighbor_black_point: u32 = (blackPoints[y as usize - 1]
+ [x as usize]
+ + (2 * blackPoints[y as usize][x as usize - 1])
+ + blackPoints[y as usize - 1][x as usize - 1])
+ / 4;
+ if (min as u32) < average_neighbor_black_point {
+ average = average_neighbor_black_point;
+ }
+ }
+ }
+ blackPoints[y as usize][x as usize] = average;
+ }
+ }
+ blackPoints
+ }
+}
\ No newline at end of file
diff --git a/src/common/input_edge.rs b/src/common/input_edge.rs
new file mode 100644
index 0000000..33f5699
--- /dev/null
+++ b/src/common/input_edge.rs
@@ -0,0 +1,115 @@
+use std::{fmt, rc::Rc};
+
+use super::{MinimalECIInput, ECIEncoderSet, ECIInput, COST_PER_ECI};
+
+pub struct InputEdge {
+ pub c: String,
+ pub encoderIndex: usize, //the encoding of this edge
+ pub previous: Option>,
+ pub cachedTotalSize: usize,
+}
+impl InputEdge {
+ pub fn new(
+ c: &str,
+ encoderSet: &ECIEncoderSet,
+ encoderIndex: usize,
+ previous: Option>,
+ fnc1: u16,
+ ) -> Self {
+ let mut size = if c == "\u{1000}" {
+ 1
+ } else {
+ encoderSet.encode_char(c, encoderIndex).len()
+ };
+
+ let fnc1Str = String::from_utf16(&[fnc1]).unwrap();
+
+ if let Some(prev) = previous {
+ let previousEncoderIndex = prev.encoderIndex;
+ if previousEncoderIndex != encoderIndex {
+ size += COST_PER_ECI;
+ }
+ size += prev.cachedTotalSize;
+
+ Self {
+ c: if c == fnc1Str {
+ String::from("\u{1000}")
+ } else {
+ String::from(c)
+ },
+ encoderIndex,
+ previous: Some(prev.clone()),
+ cachedTotalSize: size,
+ }
+ } else {
+ let previousEncoderIndex = 0;
+ if previousEncoderIndex != encoderIndex {
+ size += COST_PER_ECI;
+ }
+
+ Self {
+ c: if c == fnc1Str {
+ String::from("\u{1000}")
+ } else {
+ String::from(c)
+ },
+ encoderIndex,
+ previous: None,
+ cachedTotalSize: size,
+ }
+ }
+
+ // int size = this.c == 1000 ? 1 : encoderSet.encode(c, encoderIndex).length;
+ // let previousEncoderIndex = if previous.is_none() {
+ // 0
+ // } else {
+ // previous.unwrap().encoderIndex
+ // };
+ // int previousEncoderIndex = previous == null ? 0 : previous.encoderIndex;
+ // if previousEncoderIndex != encoderIndex {
+ // size += COST_PER_ECI;
+ // }
+ // if prev_is_some {
+ // size += previous.unwrap().cachedTotalSize;
+ // }
+
+ // Self {
+ // c: if c == fnc1 { 1000 as char } else { c },
+ // encoderIndex,
+ // previous: previous,
+ // cachedTotalSize: size,
+ // }
+ // this.c = c == fnc1 ? 1000 : c;
+ // this.encoderIndex = encoderIndex;
+ // this.previous = previous;
+ // this.cachedTotalSize = size;
+ }
+
+ pub fn isFNC1(&self) -> bool {
+ self.c == "\u{1000}"
+ }
+}
+
+impl fmt::Display for MinimalECIInput {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut result = String::new();
+ for i in 0..self.length() {
+ // for (int i = 0; i < length(); i++) {
+ if i > 0 {
+ result.push_str(", ");
+ }
+ if self.isECI(i as u32).unwrap() {
+ result.push_str("ECI(");
+ result.push_str(&self.getECIValue(i).unwrap().to_string());
+ result.push(')');
+ } else if (self.charAt(i).unwrap() as u8) < 128 {
+ result.push('\'');
+ result.push(self.charAt(i).unwrap());
+ result.push('\'');
+ } else {
+ result.push(self.charAt(i).unwrap());
+ }
+ }
+ write!(f, "{}", result)
+ }
+}
\ No newline at end of file
diff --git a/src/common/minimal_eci_input.rs b/src/common/minimal_eci_input.rs
new file mode 100644
index 0000000..a72782c
--- /dev/null
+++ b/src/common/minimal_eci_input.rs
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2021 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.nio.charset.Charset;
+// import java.util.ArrayList;
+// import java.util.List;
+
+use std::rc::Rc;
+
+use encoding::EncodingRef;
+use unicode_segmentation::UnicodeSegmentation;
+
+use crate::Exceptions;
+
+use super::{ECIEncoderSet, InputEdge, ECIInput};
+
+//* approximated (latch + 2 codewords)
+pub const COST_PER_ECI: usize = 3;
+
+/**
+ * Class that converts a character string into a sequence of ECIs and bytes
+ *
+ * The implementation uses the Dijkstra algorithm to produce minimal encodings
+ *
+ * @author Alex Geller
+ */
+pub struct MinimalECIInput {
+ bytes: Vec,
+ fnc1: u16,
+}
+
+impl ECIInput for MinimalECIInput {
+ /**
+ * Returns the length of this input. The length is the number
+ * of {@code byte}s, FNC1 characters or ECIs in the sequence.
+ *
+ * @return the number of {@code char}s in this sequence
+ */
+ fn length(&self) -> usize {
+ return self.bytes.len();
+ }
+
+ /**
+ * Returns the {@code byte} value at the specified index. An index ranges from zero
+ * to {@code length() - 1}. The first {@code byte} value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing.
+ *
+ * @param index the index of the {@code byte} value to be returned
+ *
+ * @return the specified {@code byte} value as character or the FNC1 character
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ * @throws IllegalArgumentException
+ * if the value at the {@code index} argument is an ECI (@see #isECI)
+ */
+ fn charAt(&self, index: usize) -> Result {
+ if index >= self.length() {
+ return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
+ }
+ if self.isECI(index as u32)? {
+ return Err(Exceptions::IllegalArgumentException(format!(
+ "value at {} is not a character but an ECI",
+ index
+ )));
+ }
+ if self.isFNC1(index)? {
+ Ok(self.fnc1 as u8 as char)
+ } else {
+ Ok(self.bytes[index] as u8 as char)
+ }
+ }
+
+ /**
+ * Returns a {@code CharSequence} that is a subsequence of this sequence.
+ * The subsequence starts with the {@code char} value at the specified index and
+ * ends with the {@code char} value at index {@code end - 1}. The length
+ * (in {@code char}s) of the
+ * returned sequence is {@code end - start}, so if {@code start == end}
+ * then an empty sequence is returned.
+ *
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ *
+ * @return the specified subsequence
+ *
+ * @throws IndexOutOfBoundsException
+ * if {@code start} or {@code end} are negative,
+ * if {@code end} is greater than {@code length()},
+ * or if {@code start} is greater than {@code end}
+ * @throws IllegalArgumentException
+ * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI)
+ */
+ fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions> {
+ if start > end || end > self.length() {
+ return Err(Exceptions::IndexOutOfBoundsException(start.to_string()));
+ }
+ let mut result = String::new();
+ for i in start..end {
+ // for (int i = start; i < end; i++) {
+ if self.isECI(i as u32)? {
+ return Err(Exceptions::IllegalArgumentException(format!(
+ "value at {} is not a character but an ECI",
+ i
+ )));
+ }
+ result.push_str(&self.charAt(i)?.to_string());
+ }
+ Ok(result.chars().collect())
+ }
+
+ /**
+ * Determines if a value is an ECI
+ *
+ * @param index the index of the value
+ *
+ * @return true if the value at position {@code index} is an ECI
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ */
+ fn isECI(&self, index: u32) -> Result {
+ if index >= self.length() as u32 {
+ return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
+ }
+ Ok(self.bytes[index as usize] > 255)// && self.bytes[index as usize] <= u16::MAX)
+ }
+
+ /**
+ * Returns the {@code int} ECI value at the specified index. An index ranges from zero
+ * to {@code length() - 1}. The first {@code byte} value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing.
+ *
+ * @param index the index of the {@code int} value to be returned
+ *
+ * @return the specified {@code int} ECI value.
+ * The ECI specified the encoding of all bytes with a higher index until the
+ * next ECI or until the end of the input if no other ECI follows.
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ * @throws IllegalArgumentException
+ * if the value at the {@code index} argument is not an ECI (@see #isECI)
+ */
+ fn getECIValue(&self, index: usize) -> Result {
+ if index >= self.length() {
+ return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
+ }
+ if !self.isECI(index as u32)? {
+ return Err(Exceptions::IllegalArgumentException(format!(
+ "value at {} is not an ECI but a character",
+ index
+ )));
+ }
+ Ok((self.bytes[index] as u32 - 256) as u32)
+ }
+
+ fn haveNCharacters(&self, index: usize, n: usize) -> bool {
+ if index + n - 1 >= self.bytes.len() {
+ return false;
+ }
+ for i in 0..n {
+ // for (int i = 0; i < n; i++) {
+ if self.isECI(index as u32 + i as u32).unwrap() {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+impl MinimalECIInput {
+ /**
+ * Constructs a minimal input
+ *
+ * @param stringToEncode the character string to encode
+ * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
+ * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
+ * charset to encode any character in the input that can be encoded by it if the charset is among the
+ * supported charsets.
+ * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1
+ * input.
+ */
+ pub fn new(
+ stringToEncodeInput: &str,
+ priorityCharset: Option,
+ fnc1: Option<&str>,
+ ) -> Self {
+ let stringToEncode = stringToEncodeInput.graphemes(true).collect::>();
+ let encoderSet = ECIEncoderSet::new(stringToEncodeInput, priorityCharset, fnc1);
+ let bytes = if encoderSet.len() == 1 {
+ //optimization for the case when all can be encoded without ECI in ISO-8859-1
+ let mut bytes_hld = vec![0; stringToEncode.len()];
+ for i in 0..stringToEncode.len() {
+ // for (int i = 0; i < bytes.length; i++) {
+ let c = stringToEncode.get(i).unwrap();
+ bytes_hld[i] = if fnc1.is_some() && c == fnc1.as_ref().unwrap() {
+ 1000
+ } else {
+ c.chars().nth(0).unwrap() as u16
+ };
+ }
+ bytes_hld
+ } else {
+ Self::encodeMinimally(
+ stringToEncodeInput,
+ &encoderSet,
+ fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16,
+ )
+ };
+
+ Self {
+ bytes: bytes,
+ fnc1: fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16,
+ }
+ }
+
+ pub fn getFNC1Character(&self) -> u16 {
+ self.fnc1
+ }
+
+ /**
+ * Determines if a value is the FNC1 character
+ *
+ * @param index the index of the value
+ *
+ * @return true if the value at position {@code index} is the FNC1 character
+ *
+ * @throws IndexOutOfBoundsException
+ * if the {@code index} argument is negative or not less than
+ * {@code length()}
+ */
+ pub fn isFNC1(&self, index: usize) -> Result {
+ if index >= self.length() {
+ return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
+ }
+ Ok(self.bytes[index] == 1000)
+ }
+
+ fn addEdge(edges: &mut Vec>>>, to: usize, edge: Rc) {
+ if edges[to][edge.encoderIndex].is_none()
+ || edges[to][edge.encoderIndex]
+ .clone()
+ .unwrap()
+ .cachedTotalSize
+ > edge.cachedTotalSize
+ {
+ edges[to][edge.encoderIndex] = Some(edge.clone());
+ }
+ }
+
+ fn addEdges(
+ stringToEncode: &str,
+ encoderSet: &ECIEncoderSet,
+ edges: &mut Vec>>>,
+ from: usize,
+ previous: Option>,
+ fnc1: u16,
+ ) {
+ // let ch = stringToEncode.chars().nth(from).unwrap() as i16;
+ let ch = stringToEncode.graphemes(true).nth(from).unwrap();
+
+ let mut start = 0;
+ let mut end = encoderSet.len();
+ if encoderSet.getPriorityEncoderIndex().is_some()
+ && (ch.chars().nth(0).unwrap() as u16 == fnc1
+ || encoderSet.canEncode(ch, encoderSet.getPriorityEncoderIndex().unwrap()))
+ {
+ start = encoderSet.getPriorityEncoderIndex().unwrap();
+ end = start + 1;
+ }
+
+ for i in start..end {
+ // for (int i = start; i < end; i++) {
+ if ch.chars().nth(0).unwrap() as u16 == fnc1 || encoderSet.canEncode(ch, i) {
+ Self::addEdge(
+ edges,
+ from + 1,
+ Rc::new(InputEdge::new(ch, encoderSet, i, previous.clone(), fnc1)),
+ );
+ }
+ }
+ }
+
+ pub fn encodeMinimally(
+ stringToEncode: &str,
+ encoderSet: &ECIEncoderSet,
+ fnc1: u16,
+ ) -> Vec {
+ let inputLength = stringToEncode.len();
+
+ // Array that represents vertices. There is a vertex for every character and encoding.
+ let mut edges = vec![vec![None; encoderSet.len()]; inputLength + 1]; //InputEdge[inputLength + 1][encoderSet.length()];
+ Self::addEdges(stringToEncode, encoderSet, &mut edges, 0, None, fnc1);
+
+ for i in 0..=inputLength {
+ // for (int i = 1; i <= inputLength; i++) {
+ for j in 0..encoderSet.len() {
+ // for (int j = 0; j < encoderSet.length(); j++) {
+ if edges[i][j].is_some() && i < inputLength {
+ let edg = edges[i][j].clone();
+ Self::addEdges(stringToEncode, encoderSet, &mut edges, i, edg, fnc1);
+ }
+ }
+ //optimize memory by removing edges that have been passed.
+ for j in 0..encoderSet.len() {
+ // for (int j = 0; j < encoderSet.length(); j++) {
+ edges[i - 1][j] = None;
+ }
+ }
+ let mut minimalJ: i32 = -1;
+ let mut minimalSize: i32 = i32::MAX;
+ for j in 0..encoderSet.len() {
+ // for (int j = 0; j < encoderSet.length(); j++) {
+ if edges[inputLength][j].is_some() {
+ let edge = edges[inputLength][j].clone().unwrap();
+ if (edge.cachedTotalSize as i32) < minimalSize {
+ minimalSize = edge.cachedTotalSize as i32;
+ minimalJ = j as i32;
+ }
+ }
+ }
+ if minimalJ < 0 {
+ panic!("Internal error: failed to encode \"{}\"", stringToEncode);
+ }
+ let mut intsAL: Vec = Vec::new();
+ let mut current = edges[inputLength][minimalJ as usize].clone();
+ while current.is_some() {
+ let c = current.unwrap().clone();
+ if c.isFNC1() {
+ intsAL.splice(0..0, [1000]);
+ } else {
+ let bytes: Vec = encoderSet
+ .encode_char(&c.c, c.encoderIndex)
+ .iter()
+ .map(|x| *x as u16)
+ .collect();
+ let mut i = bytes.len() as i32 - 1;
+ while i >= 0 {
+ // for (int i = bytes.length - 1; i >= 0; i--) {
+ intsAL.splice(0..0, [bytes[i as usize]]);
+ i = -1;
+ }
+ }
+ let previousEncoderIndex = if c.previous.is_none() {
+ 0
+ } else {
+ c.previous.clone().unwrap().encoderIndex
+ };
+ if previousEncoderIndex != c.encoderIndex {
+ intsAL.splice(
+ 0..0,
+ [256 as u16 + encoderSet.getECIValue(c.encoderIndex) as u16],
+ );
+ }
+ current = c.previous.clone();
+ }
+ let mut ints = vec![0; intsAL.len()];
+ for i in 0..ints.len() {
+ // for (int i = 0; i < ints.length; i++) {
+ ints[i] = *intsAL.get(i).unwrap() as u16;
+ }
+ return ints;
+ }
+}
\ No newline at end of file
diff --git a/src/common/mod.rs b/src/common/mod.rs
index c79122b..97a16ca 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -36,676 +36,13 @@ mod BitSourceTestCase;
#[cfg(test)]
mod PerspectiveTransformTestCase;
-/*
- * Copyright (C) 2010 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.nio.charset.Charset;
-// import java.nio.charset.StandardCharsets;
-// import java.util.Map;
+mod string_utils;
+pub use string_utils::*;
-/**
- * Common string-related functions.
- *
- * @author Sean Owen
- * @author Alex Dupre
- */
-pub struct StringUtils {
- // private static final Charset PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset();
- // public static final Charset SHIFT_JIS_CHARSET = Charset.forName("SJIS");
- // public static final Charset GB2312_CHARSET = Charset.forName("GB2312");
- // private static final Charset EUC_JP = Charset.forName("EUC_JP");
- // private static final boolean ASSUME_SHIFT_JIS =
- // SHIFT_JIS_CHARSET.equals(PLATFORM_DEFAULT_ENCODING) ||
- // EUC_JP.equals(PLATFORM_DEFAULT_ENCODING);
-
- // // Retained for ABI compatibility with earlier versions
- // public static final String SHIFT_JIS = "SJIS";
- // public static final String GB2312 = "GB2312";
-}
-
-// const PLATFORM_DEFAULT_ENCODING: &dyn Encoding = encoding::all::UTF_8;
-// const SHIFT_JIS_CHARSET: &dyn Encoding =
-// encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
-// const GB2312_CHARSET: &dyn Encoding =
-// encoding::label::encoding_from_whatwg_label("GB2312").unwrap();
-// const EUC_JP: &dyn Encoding = encoding::label::encoding_from_whatwg_label("EUC_JP").unwrap();
-const ASSUME_SHIFT_JIS: bool = false;
-static SHIFT_JIS: &'static str = "SJIS";
-static GB2312: &'static str = "GB2312";
-lazy_static! {
- pub static ref SHIFT_JIS_CHARSET: EncodingRef =
- encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
-}
-
-// private static final boolean ASSUME_SHIFT_JIS =
-// SHIFT_JIS_CHARSET.equals(PLATFORM_DEFAULT_ENCODING) ||
-// EUC_JP.equals(PLATFORM_DEFAULT_ENCODING);
-
-impl StringUtils {
- /**
- * @param bytes bytes encoding a string, whose encoding should be guessed
- * @param hints decode hints if applicable
- * @return name of guessed encoding; at the moment will only guess one of:
- * "SJIS", "UTF8", "ISO8859_1", or the platform default encoding if none
- * of these can possibly be correct
- */
- pub fn guessEncoding(bytes: &[u8], hints: &DecodingHintDictionary) -> String {
- let c = StringUtils::guessCharset(bytes, hints);
- if c.name()
- == encoding::label::encoding_from_whatwg_label("SJIS")
- .unwrap()
- .name()
- {
- return "SJIS".to_owned();
- } else if c.name() == encoding::all::UTF_8.name() {
- return "UTF8".to_owned();
- } else if c.name() == encoding::all::ISO_8859_1.name() {
- return "ISO8859_1".to_owned();
- }
- return c.name().to_owned();
- }
-
- /**
- * @param bytes bytes encoding a string, whose encoding should be guessed
- * @param hints decode hints if applicable
- * @return Charset of guessed encoding; at the moment will only guess one of:
- * {@link #SHIFT_JIS_CHARSET}, {@link StandardCharsets#UTF_8},
- * {@link StandardCharsets#ISO_8859_1}, {@link StandardCharsets#UTF_16},
- * or the platform default encoding if
- * none of these can possibly be correct
- */
- pub fn guessCharset(bytes: &[u8], hints: &DecodingHintDictionary) -> EncodingRef {
- match hints.get(&DecodeHintType::CHARACTER_SET) {
- Some(hint) => {
- if let DecodeHintValue::CharacterSet(cs_name) = hint {
- return encoding::label::encoding_from_whatwg_label(cs_name).unwrap();
- }
- }
- _ => {}
- };
- // if hints.contains_key(&DecodeHintType::CHARACTER_SET) {
- // return Charset.forName(hints.get(DecodeHintType.CHARACTER_SET).toString());
- // }
-
- // First try UTF-16, assuming anything with its BOM is UTF-16
- if bytes.len() > 2
- && ((bytes[0] == 0xFE && bytes[1] == 0xFF) || (bytes[0] == 0xFF && bytes[1] == 0xFE))
- {
- if bytes[0] == 0xFE && bytes[1] == 0xFF {
- return encoding::all::UTF_16BE;
- } else {
- return encoding::all::UTF_16LE;
- }
- }
-
- // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
- // which should be by far the most common encodings.
- let length = bytes.len();
- let mut can_be_iso88591 = true;
- let mut can_be_shift_jis = true;
- let mut can_be_utf8 = true;
- let mut utf8_bytes_left = 0;
- let mut utf2_bytes_chars = 0;
- let mut utf3_bytes_chars = 0;
- let mut utf4_bytes_chars = 0;
- let mut sjis_bytes_left = 0;
- let mut sjis_katakana_chars = 0;
- let mut sjis_cur_katakana_word_length = 0;
- let mut sjis_cur_double_bytes_word_length = 0;
- let mut sjis_max_katakana_word_length = 0;
- let mut sjis_max_double_bytes_word_length = 0;
- let mut iso_high_other = 0;
-
- let utf8bom = bytes.len() > 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF;
-
- for i in 0..length {
- // for (int i = 0;
- // i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
- // i++) {
- if !(can_be_iso88591 || can_be_shift_jis || can_be_utf8) {
- break;
- }
-
- let value = bytes[i];
-
- // UTF-8 stuff
- if can_be_utf8 {
- if utf8_bytes_left > 0 {
- if (value & 0x80) == 0 {
- can_be_utf8 = false;
- } else {
- utf8_bytes_left -= 1;
- }
- } else if (value & 0x80) != 0 {
- if (value & 0x40) == 0 {
- can_be_utf8 = false;
- } else {
- utf8_bytes_left += 1;
- if (value & 0x20) == 0 {
- utf2_bytes_chars += 1;
- } else {
- utf8_bytes_left += 1;
- if (value & 0x10) == 0 {
- utf3_bytes_chars += 1;
- } else {
- utf8_bytes_left += 1;
- if (value & 0x08) == 0 {
- utf4_bytes_chars += 1;
- } else {
- can_be_utf8 = false;
- }
- }
- }
- }
- }
- }
-
- // ISO-8859-1 stuff
- if can_be_iso88591 {
- if value > 0x7F && value < 0xA0 {
- can_be_iso88591 = false;
- } else if value > 0x9F && (value < 0xC0 || value == 0xD7 || value == 0xF7) {
- iso_high_other += 1;
- }
- }
-
- // Shift_JIS stuff
- if can_be_shift_jis {
- if sjis_bytes_left > 0 {
- if value < 0x40 || value == 0x7F || value > 0xFC {
- can_be_shift_jis = false;
- } else {
- sjis_bytes_left -= 1;
- }
- } else if value == 0x80 || value == 0xA0 || value > 0xEF {
- can_be_shift_jis = false;
- } else if value > 0xA0 && value < 0xE0 {
- sjis_katakana_chars += 1;
- sjis_cur_double_bytes_word_length = 0;
- sjis_cur_katakana_word_length += 1;
- if sjis_cur_katakana_word_length > sjis_max_katakana_word_length {
- sjis_max_katakana_word_length = sjis_cur_katakana_word_length;
- }
- } else if value > 0x7F {
- sjis_bytes_left += 1;
- //sjisDoubleBytesChars++;
- sjis_cur_katakana_word_length = 0;
- sjis_cur_double_bytes_word_length += 1;
- if sjis_cur_double_bytes_word_length > sjis_max_double_bytes_word_length {
- sjis_max_double_bytes_word_length = sjis_cur_double_bytes_word_length;
- }
- } else {
- //sjisLowChars++;
- sjis_cur_katakana_word_length = 0;
- sjis_cur_double_bytes_word_length = 0;
- }
- }
- }
-
- if can_be_utf8 && utf8_bytes_left > 0 {
- can_be_utf8 = false;
- }
- if can_be_shift_jis && sjis_bytes_left > 0 {
- can_be_shift_jis = false;
- }
-
- // Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done
- if can_be_utf8 && (utf8bom || utf2_bytes_chars + utf3_bytes_chars + utf4_bytes_chars > 0) {
- return encoding::all::UTF_8;
- }
- // Easy -- if assuming Shift_JIS or >= 3 valid consecutive not-ascii characters (and no evidence it can't be), done
- if can_be_shift_jis
- && (ASSUME_SHIFT_JIS
- || sjis_max_katakana_word_length >= 3
- || sjis_max_double_bytes_word_length >= 3)
- {
- return encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
- }
- // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is:
- // - If we saw
- // - only two consecutive katakana chars in the whole text, or
- // - at least 10% of bytes that could be "upper" not-alphanumeric Latin1,
- // - then we conclude Shift_JIS, else ISO-8859-1
- if can_be_iso88591 && can_be_shift_jis {
- return if (sjis_max_katakana_word_length == 2 && sjis_katakana_chars == 2)
- || iso_high_other * 10 >= length
- {
- encoding::label::encoding_from_whatwg_label("SJIS").unwrap()
- } else {
- encoding::all::ISO_8859_1
- };
- }
-
- // Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding
- if can_be_iso88591 {
- return encoding::all::ISO_8859_1;
- }
- if can_be_shift_jis {
- return encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
- }
- if can_be_utf8 {
- return encoding::all::UTF_8;
- }
- // Otherwise, we take a wild guess with platform encoding
- return encoding::all::UTF_8;
- }
-}
-
-/*
- * 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;
-
-static EMPTY_BITS: [u32; 0] = [0; 0];
-static LOAD_FACTOR: f32 = 0.75f32;
-
-/**
- * A simple, fast array of bits, represented compactly by an array of ints internally.
- *
- * @author Sean Owen
- */
-#[derive(Debug, PartialEq, Eq, Clone, Hash)]
-pub struct BitArray {
- bits: Vec,
- size: usize,
-}
-
-impl BitArray {
- pub fn new() -> Self {
- Self {
- bits: EMPTY_BITS.to_vec(),
- size: 0,
- }
- }
-
- pub fn with_size(size: usize) -> Self {
- Self {
- bits: BitArray::makeArray(size),
- size: size,
- }
- }
-
- // For testing only
- pub fn with_initial_values(bits: Vec, size: usize) -> Self {
- Self {
- bits: bits,
- size: size,
- }
- }
-
- pub fn getSize(&self) -> usize {
- self.size
- }
-
- pub fn getSizeInBytes(&self) -> usize {
- return (self.size + 7) / 8;
- }
-
- fn ensure_capacity(&mut self, newSize: usize) {
- if newSize > self.bits.len() * 32 {
- let mut newBits = BitArray::makeArray((newSize as f32 / LOAD_FACTOR).ceil() as usize);
- //System.arraycopy(bits, 0, newBits, 0, bits.length);
- newBits[0..self.bits.len()].clone_from_slice(&self.bits[0..self.bits.len()]);
- self.bits = newBits;
- }
- }
-
- /**
- * @param i bit to get
- * @return true iff bit i is set
- */
- pub fn get(&self, i: usize) -> bool {
- return (self.bits[i / 32] & (1 << (i & 0x1F))) != 0;
- }
-
- /**
- * Sets bit i.
- *
- * @param i bit to set
- */
- pub fn set(&mut self, i: usize) {
- self.bits[i / 32] |= 1 << (i & 0x1F);
- }
-
- /**
- * Flips bit i.
- *
- * @param i bit to set
- */
- pub fn flip(&mut self, i: usize) {
- self.bits[i / 32] ^= 1 << (i & 0x1F);
- }
-
- /**
- * @param from first bit to check
- * @return index of first bit that is set, starting from the given index, or size if none are set
- * at or beyond this given index
- * @see #getNextUnset(int)
- */
- pub fn getNextSet(&self, from: usize) -> usize {
- if from >= self.size {
- return self.size;
- }
- let mut bitsOffset = from / 32;
- let mut currentBits = self.bits[bitsOffset] as i64;
- // mask off lesser bits first
- currentBits &= -(1 << (from & 0x1F));
- while currentBits == 0 {
- bitsOffset += 1;
- if bitsOffset == self.bits.len() {
- return self.size;
- }
- currentBits = self.bits[bitsOffset] as i64;
- }
- let result = (bitsOffset * 32) + currentBits.trailing_zeros() as usize;
- cmp::min(result, self.size)
- }
-
- /**
- * @param from index to start looking for unset bit
- * @return index of next unset bit, or {@code size} if none are unset until the end
- * @see #getNextSet(int)
- */
- pub fn getNextUnset(&self, from: usize) -> usize {
- if from >= self.size {
- return self.size;
- }
- let mut bitsOffset = from / 32;
- let mut currentBits = !self.bits[bitsOffset] as i32;
- // mask off lesser bits first
- currentBits &= -(1 << (from & 0x1F));
- while currentBits == 0 {
- bitsOffset += 1;
- if bitsOffset == self.bits.len() {
- return self.size;
- }
- currentBits = !self.bits[bitsOffset] as i32;
- }
- let result = (bitsOffset * 32) + currentBits.trailing_zeros() as usize;
- return cmp::min(result, self.size);
- }
-
- /**
- * Sets a block of 32 bits, starting at bit i.
- *
- * @param i first bit to set
- * @param newBits the new value of the next 32 bits. Note again that the least-significant bit
- * corresponds to bit i, the next-least-significant to i+1, and so on.
- */
- pub fn setBulk(&mut self, i: usize, newBits: u32) {
- self.bits[i / 32] = newBits;
- }
-
- /**
- * Sets a range of bits.
- *
- * @param start start of range, inclusive.
- * @param end end of range, exclusive
- */
- pub fn setRange(&mut self, start: usize, end: usize) -> Result<(), Exceptions> {
- let mut end = end;
- if end < start || end > self.size {
- return Err(Exceptions::IllegalArgumentException(
- "end < start || start < 0 || end > self.size".to_owned(),
- ));
- }
- if end == start {
- return Ok(());
- }
- end -= 1; // will be easier to treat this as the last actually set bit -- inclusive
- let firstInt = start / 32;
- let lastInt = end / 32;
- for i in firstInt..=lastInt {
- //for (int i = firstInt; i <= lastInt; i++) {
- let firstBit = if i > firstInt { 0 } else { start & 0x1F };
- let lastBit = if i < lastInt { 31 } else { end & 0x1F };
- // Ones from firstBit to lastBit, inclusive
- let mask: u64 = (2 << lastBit) - (1 << firstBit);
- self.bits[i] |= mask as u32;
- }
- Ok(())
- }
-
- /**
- * Clears all bits (sets to false).
- */
- pub fn clear(&mut self) {
- let max = self.bits.len();
- for i in 0..max {
- //for (int i = 0; i < max; i++) {
- self.bits[i] = 0;
- }
- }
-
- /**
- * Efficient method to check if a range of bits is set, or not set.
- *
- * @param start start of range, inclusive.
- * @param end end of range, exclusive
- * @param value if true, checks that bits in range are set, otherwise checks that they are not set
- * @return true iff all bits are set or not set in range, according to value argument
- * @throws IllegalArgumentException if end is less than start or the range is not contained in the array
- */
- pub fn isRange(&self, start: usize, end: usize, value: bool) -> Result {
- let mut end = end;
- if end < start || end > self.size {
- return Err(Exceptions::IllegalArgumentException(
- "end < start || start < 0 || end > self.size".to_owned(),
- ));
- }
- if end == start {
- return Ok(true); // empty range matches
- }
- end -= 1; // will be easier to treat this as the last actually set bit -- inclusive
- let firstInt = start / 32;
- let lastInt = end / 32;
- for i in firstInt..=lastInt {
- //for (int i = firstInt; i <= lastInt; i++) {
- let firstBit = if i > firstInt { 0 } else { start & 0x1F };
- let lastBit = if i < lastInt { 31 } else { end & 0x1F };
- // Ones from firstBit to lastBit, inclusive
- let mask: u64 = (2 << lastBit) - (1 << firstBit);
-
- // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
- // equals the mask, or we're looking for 0s and the masked portion is not all 0s
- if (self.bits[i] & mask as u32) != (if value { mask as u32 } else { 0 }) {
- return Ok(false);
- }
- }
- return Ok(true);
- }
-
- pub fn appendBit(&mut self, bit: bool) {
- self.ensure_capacity(self.size + 1);
- if bit {
- self.bits[self.size / 32] |= 1 << (self.size & 0x1F);
- }
- self.size += 1;
- }
-
- /**
- * 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.
- *
- * @param value {@code int} containing bits to append
- * @param numBits bits from value to append
- */
- pub fn appendBits(&mut self, value: u32, num_bits: usize) -> Result<(), Exceptions> {
- if num_bits > 32 {
- return Err(Exceptions::IllegalArgumentException(
- "Num bits must be between 0 and 32".to_owned(),
- ));
- }
-
- if num_bits == 0 {
- return Ok(());
- }
-
- let mut next_size = self.size;
- self.ensure_capacity(next_size + num_bits);
- for numBitsLeft in (0..num_bits).rev() {
- //for (int numBitsLeft = numBits - 1; numBitsLeft >= 0; numBitsLeft--) {
- if (value & (1 << numBitsLeft)) != 0 {
- self.bits[next_size / 32] |= 1 << (next_size & 0x1F);
- }
- next_size += 1;
- }
- self.size = next_size;
- Ok(())
- }
-
- pub fn appendBitArray(&mut self, other: BitArray) {
- let otherSize = other.size;
- self.ensure_capacity(self.size + otherSize);
- for i in 0..otherSize {
- //for (int i = 0; i < otherSize; i++) {
- self.appendBit(other.get(i));
- }
- }
-
- pub fn xor(&mut self, other: &BitArray) -> Result<(), Exceptions> {
- if self.size != other.size {
- return Err(Exceptions::IllegalArgumentException(
- "Sizes don't match".to_owned(),
- ));
- }
- for i in 0..self.bits.len() {
- //for (int i = 0; i < bits.length; i++) {
- // The last int could be incomplete (i.e. not have 32 bits in
- // it) but there is no problem since 0 XOR 0 == 0.
- self.bits[i] ^= other.bits[i];
- }
- Ok(())
- }
-
- /**
- *
- * @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
- */
- pub fn toBytes(&self, bitOffset: usize, array: &mut [u8], offset: usize, numBytes: usize) {
- let mut bitOffset = bitOffset;
- for i in 0..numBytes {
- //for (int i = 0; i < numBytes; i++) {
- let mut theByte = 0;
- for j in 0..8 {
- //for (int j = 0; j < 8; j++) {
- if self.get(bitOffset) {
- theByte |= 1 << (7 - j);
- }
- bitOffset += 1;
- }
- array[offset + i] = theByte;
- }
- }
-
- /**
- * @return underlying array of ints. The first element holds the first 32 bits, and the least
- * significant bit is bit 0.
- */
- pub fn getBitArray(&self) -> &Vec {
- return &self.bits;
- }
-
- /**
- * Reverses all bits in the array.
- */
- pub fn reverse(&mut self) {
- let mut newBits = vec![0; self.bits.len()];
- // reverse all int's first
- let len = (self.size - 1) / 32;
- let oldBitsLen = len + 1;
- for i in 0..oldBitsLen {
- //for (int i = 0; i < oldBitsLen; i++) {
- newBits[len - i] = self.bits[i].reverse_bits();
- }
- // now correct the int's if the bit size isn't a multiple of 32
- if self.size != oldBitsLen * 32 {
- let leftOffset = oldBitsLen * 32 - self.size;
- let mut currentInt = newBits[0] >> leftOffset;
- for i in 1..oldBitsLen {
- //for (int i = 1; i < oldBitsLen; i++) {
- let nextInt = newBits[i];
- currentInt |= nextInt << (32 - leftOffset);
- newBits[i - 1] = currentInt;
- currentInt = nextInt >> leftOffset;
- }
- newBits[oldBitsLen - 1] = currentInt;
- }
- self.bits = newBits;
- }
-
- fn makeArray(size: usize) -> Vec {
- return vec![0; (size + 31) / 32];
- }
-
- // @Override
- // public boolean equals(Object o) {
- // if (!(o instanceof BitArray)) {
- // return false;
- // }
- // BitArray other = (BitArray) o;
- // return size == other.size && Arrays.equals(bits, other.bits);
- // }
-
- // @Override
- // public int hashCode() {
- // return 31 * size + Arrays.hashCode(bits);
- // }
-
- // @Override
- // public BitArray clone() {
- // return new BitArray(bits.clone(), size);
- // }
-}
-
-impl fmt::Display for BitArray {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut _str = String::with_capacity(self.size + (self.size / 8) + 1);
- for i in 0..self.size {
- //for (int i = 0; i < size; i++) {
- if (i & 0x07) == 0 {
- _str.push_str(" ");
- }
- _str.push_str(if self.get(i) { "X" } else { "." });
- }
- write!(f, "{}", _str)
- }
-}
+mod bit_array;
+pub use bit_array::*;
/*
* Copyright 2007 ZXing authors
@@ -745,3398 +82,48 @@ pub trait DetectorRXingResult {
// points: Vec,
// }
-/*
- * 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.
- */
+mod bit_matrix;
+pub use bit_matrix::*;
-// package com.google.zxing.common;
+mod eci_input;
+pub use eci_input::*;
-// import java.util.Arrays;
+mod bit_source;
+pub use bit_source::*;
-/**
- * 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, PartialEq, Eq, Hash)]
-pub struct BitMatrix {
- width: u32,
- height: u32,
- row_size: usize,
- bits: Vec,
-}
+mod perspective_transform;
+pub use perspective_transform::*;
-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).unwrap()
- }
+mod decoder_rxing_result;
+pub use decoder_rxing_result::*;
- /**
- * 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(Exceptions::IllegalArgumentException(
- "Both dimensions must be greater than 0".to_owned(),
- ));
- }
- Ok(Self {
- width,
- height,
- row_size: ((width + 31) / 32) as usize,
- bits: vec![0; (((width + 31) / 32) * height) as usize],
- })
- // this.width = width;
- // this.height = height;
- // this.rowSize = (width + 31) / 32;
- // bits = new int[rowSize * height];
- }
+mod bit_source_builder;
+pub use bit_source_builder::*;
- fn with_all_data(&self, width: u32, height: u32, rowSize: usize, bits: Vec) -> Self {
- Self {
- width,
- height,
- row_size: rowSize,
- bits,
- }
- }
+mod grid_sampler;
+pub use grid_sampler::*;
- /**
- * 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_bools(image: &Vec>) -> Self {
- let height: u32 = image.len().try_into().unwrap();
- let width: u32 = image[0].len().try_into().unwrap();
- let mut bits = BitMatrix::new(width, height).unwrap();
- for i in 0..height as usize {
- //for (int i = 0; i < height; i++) {
- let imageI = &image[i];
- for j in 0..width as usize {
- //for (int j = 0; j < width; j++) {
- if imageI[j] {
- bits.set(j as u32, i as u32);
- }
- }
- }
- return bits;
- }
+mod default_grid_sampler;
+pub use default_grid_sampler::*;
- pub fn parse_strings(
- string_representation: &str,
- set_string: &str,
- unset_string: &str,
- ) -> Result {
- // cannot pass nulls in rust
- // if (stringRepresentation == null) {
- // throw new IllegalArgumentException();
- // }
+mod character_set_eci;
+pub use character_set_eci::*;
- let mut bits = vec![false; string_representation.len()];
- let mut bitsPos = 0;
- let mut rowStartPos = 0;
- let mut rowLength = 0; //-1;
- let mut first_run = true;
- let mut nRows = 0;
- let mut pos = 0;
- while pos < string_representation.len() {
- if string_representation.chars().nth(pos).unwrap() == '\n'
- || string_representation.chars().nth(pos).unwrap() == '\r'
- {
- if bitsPos > rowStartPos {
- //if rowLength == -1 {
- if first_run {
- first_run = false;
- rowLength = bitsPos - rowStartPos;
- } else if bitsPos - rowStartPos != rowLength {
- return Err(Exceptions::IllegalArgumentException(
- "row lengths do not match".to_owned(),
- ));
- }
- rowStartPos = bitsPos;
- nRows += 1;
- }
- pos += 1;
- } else if string_representation[pos..].starts_with(set_string) {
- pos += set_string.len();
- bits[bitsPos] = true;
- bitsPos += 1;
- } else if string_representation[pos..].starts_with(unset_string) {
- pos += unset_string.len();
- bits[bitsPos] = false;
- bitsPos += 1;
- } else {
- return Err(Exceptions::IllegalArgumentException(format!(
- "illegal character encountered: {}",
- string_representation[pos..].to_owned()
- )));
- }
- }
+mod eci_string_builder;
+pub use eci_string_builder::*;
- // no EOL at end?
- if bitsPos > rowStartPos {
- //if rowLength == -1 {
- if first_run {
- first_run = false;
- rowLength = bitsPos - rowStartPos;
- } else if bitsPos - rowStartPos != rowLength {
- return Err(Exceptions::IllegalArgumentException(
- "row lengths do not match".to_owned(),
- ));
- }
- nRows += 1;
- }
+mod eci_encoder_set;
+pub use eci_encoder_set::*;
- let mut matrix = BitMatrix::new(rowLength.try_into().unwrap(), nRows)?;
- for i in 0..bitsPos {
- //for (int i = 0; i < bitsPos; i++) {
- if bits[i] {
- matrix.set(
- (i % rowLength).try_into().unwrap(),
- (i / rowLength).try_into().unwrap(),
- );
- }
- }
- return Ok(matrix);
- }
+mod minimal_eci_input;
+pub use minimal_eci_input::*;
- /**
- * 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 as usize * self.row_size + (x as usize / 32);
- return ((self.bits[offset] >> (x & 0x1f)) & 1) != 0;
- }
+mod input_edge;
+pub use input_edge::*;
- /**
- * 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(&mut self, x: u32, y: u32) {
- let offset = y as usize * self.row_size + (x as usize / 32);
- self.bits[offset] |= 1 << (x & 0x1f);
- }
- pub fn unset(&mut self, x: u32, y: u32) {
- let offset = y as usize * self.row_size + (x as usize / 32);
- self.bits[offset] &= !(1 << (x & 0x1f));
- }
+mod global_histogram_binarizer;
+pub use global_histogram_binarizer::*;
- /**
- * 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_coords(&mut self, x: u32, y: u32) {
- let offset = y as usize * self.row_size + (x as usize / 32);
- self.bits[offset] ^= 1 << (x & 0x1f);
- }
-
- /**
- * Flips every bit in the matrix.
- */
- pub fn flip_self(&mut 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(&mut self, mask: &BitMatrix) -> Result<(), Exceptions> {
- if self.width != mask.width || self.height != mask.height || self.row_size != mask.row_size
- {
- return Err(Exceptions::IllegalArgumentException(
- "input matrix dimensions do not match".to_owned(),
- ));
- }
- let rowArray = BitArray::with_size(self.width as usize);
- for y in 0..self.height {
- //for (int y = 0; y < height; y++) {
- let offset = y as usize * self.row_size;
- let tmp = mask.getRow(y, &rowArray);
- let row = tmp.getBitArray();
- for x in 0..self.row_size {
- //for (int x = 0; x < rowSize; x++) {
- self.bits[offset + x] ^= row[x];
- }
- }
- Ok(())
- }
-
- /**
- * Clears all bits (sets to false).
- */
- pub fn clear(&mut 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(
- &mut self,
- left: u32,
- top: u32,
- width: u32,
- height: u32,
- ) -> Result<(), Exceptions> {
- // if top < 0 || left < 0 {
- // return Err(Exceptions::IllegalArgumentException(
- // "Left and top must be nonnegative".to_owned(),
- // ));
- // }
- if height < 1 || width < 1 {
- return Err(Exceptions::IllegalArgumentException(
- "Height and width must be at least 1".to_owned(),
- ));
- }
- let right = left + width;
- let bottom = top + height;
- if bottom > self.height || right > self.width {
- return Err(Exceptions::IllegalArgumentException(
- "The region must fit inside the matrix".to_owned(),
- ));
- }
- for y in top..bottom {
- //for (int y = top; y < bottom; y++) {
- let offset = y as usize * self.row_size;
- for x in left..right {
- //for (int x = left; x < right; x++) {
- self.bits[offset + (x as usize / 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 mut rw: BitArray = if row.getSize() < self.width as usize {
- BitArray::with_size(self.width as usize)
- } else {
- let mut z = row.clone();
- z.clear();
- z
- // row.clear();
- // row.clone()
- };
-
- let offset = y as usize * self.row_size;
- for x in 0..self.row_size {
- //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(&mut self, y: u32, row: &BitArray) {
- return self.bits[y as usize * self.row_size..y as usize * self.row_size + self.row_size]
- .clone_from_slice(&row.getBitArray()[0..self.row_size]);
- //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(&mut self, degrees: u32) -> Result<(), Exceptions> {
- match degrees % 360 {
- 0 => Ok(()),
- 90 => {
- self.rotate90();
- Ok(())
- }
- 180 => {
- self.rotate180();
- Ok(())
- }
- 270 => {
- self.rotate90();
- self.rotate180();
- Ok(())
- }
- _ => Err(Exceptions::IllegalArgumentException(
- "degrees must be a multiple of 0, 90, 180, or 270".to_owned(),
- )),
- }
- }
-
- /**
- * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
- */
- pub fn rotate180(&mut self) {
- let mut topRow = BitArray::with_size(self.width as usize);
- let mut bottomRow = BitArray::with_size(self.width as usize);
- let 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(&mut self) {
- let newWidth = self.height;
- let newHeight = self.width;
- let newRowSize = (newWidth + 31) / 32;
- let mut newBits = vec![0; (newRowSize * newHeight).try_into().unwrap()];
-
- 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 as usize * self.row_size + (x as usize / 32);
- if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 {
- let newOffset: usize = ((newHeight - 1 - x) * newRowSize + (y / 32))
- .try_into()
- .unwrap();
- newBits[newOffset] |= 1 << (y & 0x1f);
- }
- }
- }
- self.width = newWidth;
- self.height = newHeight;
- self.row_size = newRowSize.try_into().unwrap();
- 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 mut left = self.width;
- let mut top = self.height;
- // let right = -1;
- // let bottom = -1;
- let mut right: u32 = 0;
- let mut bottom = 0;
-
- for y in 0..self.height {
- //for (int y = 0; y < height; y++) {
- for x32 in 0..self.row_size {
- //for (int x32 = 0; x32 < rowSize; x32++) {
- let theBits = self.bits[y as usize * self.row_size + x32];
- if theBits != 0 {
- if y < top {
- top = y;
- }
- if y > bottom {
- bottom = y;
- }
- if x32 * 32 < left.try_into().unwrap() {
- let mut bit = 0;
- while (theBits << (31 - bit)) == 0 {
- bit += 1;
- }
- if (x32 * 32 + bit) < left.try_into().unwrap() {
- left = (x32 * 32 + bit).try_into().unwrap();
- }
- }
- if x32 * 32 + 31 > right.try_into().unwrap() {
- let mut bit = 31;
- while (theBits >> bit) == 0 {
- bit -= 1;
- }
- if (x32 * 32 + bit) > right.try_into().unwrap() {
- right = (x32 * 32 + bit).try_into().unwrap();
- }
- }
- }
- }
- }
-
- 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 mut bitsOffset = 0;
- while bitsOffset < self.bits.len() && self.bits[bitsOffset] == 0 {
- bitsOffset += 1;
- }
- if bitsOffset == self.bits.len() {
- return None;
- }
- let y = bitsOffset / self.row_size;
- let mut x = (bitsOffset % self.row_size) * 32;
-
- let theBits = self.bits[bitsOffset];
- let mut bit = 0;
- while (theBits << (31 - bit)) == 0 {
- bit += 1;
- }
- x += bit;
- return Some(vec![x as u32, y as u32]);
- }
-
- pub fn getBottomRightOnBit(&self) -> Option> {
- let mut bitsOffset = self.bits.len() as i64 - 1;
- while bitsOffset >= 0 && self.bits[bitsOffset as usize] == 0 {
- bitsOffset -= 1;
- }
- if bitsOffset < 0 {
- return None;
- }
-
- let y = bitsOffset as usize / self.row_size;
- let mut x = (bitsOffset as usize % self.row_size) * 32;
-
- let theBits = self.bits[bitsOffset as usize];
- let mut bit = 31;
- while (theBits >> bit) == 0 {
- bit -= 1;
- }
- x += bit;
-
- return Some(vec![x as u32, y as u32]);
- }
-
- /**
- * @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.row_size;
- }
-
- // @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 mut result =
- String::with_capacity((self.height * (self.width + 1)).try_into().unwrap());
- 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 ", " "))
- }
-}
-
-/*
- * Copyright 2021 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;
-
-/**
- * Interface to navigate a sequence of ECIs and bytes.
- *
- * @author Alex Geller
- */
-pub trait ECIInput {
- /**
- * Returns the length of this input. The length is the number
- * of {@code byte}s in or ECIs in the sequence.
- *
- * @return the number of {@code char}s in this sequence
- */
- fn length(&self) -> usize;
-
- /**
- * Returns the {@code byte} value at the specified index. An index ranges from zero
- * to {@code length() - 1}. The first {@code byte} value of the sequence is at
- * index zero, the next at index one, and so on, as for array
- * indexing.
- *
- * @param index the index of the {@code byte} value to be returned
- *
- * @return the specified {@code byte} value as character or the FNC1 character
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- * @throws IllegalArgumentException
- * if the value at the {@code index} argument is an ECI (@see #isECI)
- */
- fn charAt(&self, index: usize) -> Result;
-
- /**
- * Returns a {@code CharSequence} that is a subsequence of this sequence.
- * The subsequence starts with the {@code char} value at the specified index and
- * ends with the {@code char} value at index {@code end - 1}. The length
- * (in {@code char}s) of the
- * returned sequence is {@code end - start}, so if {@code start == end}
- * then an empty sequence is returned.
- *
- * @param start the start index, inclusive
- * @param end the end index, exclusive
- *
- * @return the specified subsequence
- *
- * @throws IndexOutOfBoundsException
- * if {@code start} or {@code end} are negative,
- * if {@code end} is greater than {@code length()},
- * or if {@code start} is greater than {@code end}
- * @throws IllegalArgumentException
- * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI)
- */
- fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions>;
-
- /**
- * Determines if a value is an ECI
- *
- * @param index the index of the value
- *
- * @return true if the value at position {@code index} is an ECI
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- */
- fn isECI(&self, index: u32) -> Result;
-
- /**
- * Returns the {@code int} ECI value at the specified index. An index ranges from zero
- * to {@code length() - 1}. The first {@code byte} value of the sequence is at
- * index zero, the next at index one, and so on, as for array
- * indexing.
- *
- * @param index the index of the {@code int} value to be returned
- *
- * @return the specified {@code int} ECI value.
- * The ECI specified the encoding of all bytes with a higher index until the
- * next ECI or until the end of the input if no other ECI follows.
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- * @throws IllegalArgumentException
- * if the value at the {@code index} argument is not an ECI (@see #isECI)
- */
- fn getECIValue(&self, index: usize) -> Result;
- fn haveNCharacters(&self, index: usize, n: usize) -> bool;
-}
-
-/*
- * 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;
-
-/**
- * This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
- * number of bits read is not often a multiple of 8.
- *
- * This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
- * it passed in, in which case all bets are off.
- *
- * @author Sean Owen
- */
-pub struct BitSource {
- bytes: Vec,
- byte_offset: usize,
- bit_offset: usize,
-}
-
-impl BitSource {
- /**
- * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
- * Bits are read within a byte from most-significant to least-significant bit.
- */
- pub fn new(bytes: Vec) -> Self {
- Self {
- bytes,
- byte_offset: 0,
- bit_offset: 0,
- }
- }
-
- /**
- * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
- */
- pub fn getBitOffset(&self) -> usize {
- return self.bit_offset;
- }
-
- /**
- * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
- */
- pub fn getByteOffset(&self) -> usize {
- return self.byte_offset;
- }
-
- /**
- * @param numBits number of bits to read
- * @return int representing the bits read. The bits will appear as the least-significant
- * bits of the int
- * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available
- */
- pub fn readBits(&mut self, numBits: usize) -> Result {
- if numBits < 1 || numBits > 32 || numBits > self.available() {
- return Err(Exceptions::IllegalArgumentException(numBits.to_string()));
- }
-
- let mut result: u32 = 0;
-
- let mut num_bits = numBits;
-
- // First, read remainder from current byte
- if self.bit_offset > 0 {
- let bitsLeft = 8 - self.bit_offset;
- let toRead = cmp::min(num_bits, bitsLeft);
- let bitsToNotRead = bitsLeft - toRead;
- let mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
-
- result = (self.bytes[self.byte_offset] & mask) as u32 >> bitsToNotRead;
- num_bits -= toRead;
- self.bit_offset += toRead;
- if self.bit_offset == 8 {
- self.bit_offset = 0;
- self.byte_offset += 1;
- }
- }
-
- // Next read whole bytes
- if num_bits > 0 {
- while num_bits >= 8 {
- result = (result << 8) | (self.bytes[self.byte_offset] & 0xFF) as u32;
- // result = ((result as u16) << 8) as u8 | (self.bytes[self.byte_offset]);
- self.byte_offset += 1;
- num_bits -= 8;
- }
-
- // Finally read a partial byte
- if num_bits > 0 {
- let bits_to_not_read = 8 - num_bits;
- let mask = (0xFF >> bits_to_not_read) << bits_to_not_read;
- result = (result << num_bits)
- | ((self.bytes[self.byte_offset] & mask) as u32 >> bits_to_not_read);
- self.bit_offset += num_bits;
- }
- }
-
- return Ok(result);
- }
-
- /**
- * @return number of bits that can be read successfully
- */
- pub fn available(&self) -> usize {
- return 8 * (self.bytes.len() - self.byte_offset) - self.bit_offset;
- }
-}
-
-/*
- * 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;
-
-/**
- * This class implements a perspective transform in two dimensions. Given four source and four
- * destination points, it will compute the transformation implied between them. The code is based
- * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
- *
- * @author Sean Owen
- */
-pub struct PerspectiveTransform {
- a11: f32,
- a12: f32,
- a13: f32,
- a21: f32,
- a22: f32,
- a23: f32,
- a31: f32,
- a32: f32,
- a33: f32,
-}
-
-impl PerspectiveTransform {
- fn new(
- a11: f32,
- a21: f32,
- a31: f32,
- a12: f32,
- a22: f32,
- a32: f32,
- a13: f32,
- a23: f32,
- a33: f32,
- ) -> Self {
- Self {
- a11,
- a12,
- a13,
- a21,
- a22,
- a23,
- a31,
- a32,
- a33,
- }
- }
-
- pub fn quadrilateralToQuadrilateral(
- x0: f32,
- y0: f32,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- x3: f32,
- y3: f32,
- x0p: f32,
- y0p: f32,
- x1p: f32,
- y1p: f32,
- x2p: f32,
- y2p: f32,
- x3p: f32,
- y3p: f32,
- ) -> Self {
- let q_to_s = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
- let s_to_q =
- PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
- return s_to_q.times(&q_to_s);
- }
-
- pub fn transform_points_single(&self, points: &mut [f32]) {
- let a11 = self.a11;
- let a12 = self.a12;
- let a13 = self.a13;
- let a21 = self.a21;
- let a22 = self.a22;
- let a23 = self.a23;
- let a31 = self.a31;
- let a32 = self.a32;
- let a33 = self.a33;
- let maxI = points.len() - 1; // points.length must be even
- let mut i = 0;
- while i < maxI {
- // for (int i = 0; i < maxI; i += 2) {
- let x = points[i];
- let y = points[i + 1];
- let denominator = a13 * x + a23 * y + a33;
- points[i] = (a11 * x + a21 * y + a31) / denominator;
- points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
- i += 2;
- }
- }
-
- pub fn transform_points_double(&self, x_values: &mut [f32], y_valuess: &mut [f32]) {
- let n = x_values.len();
- for i in 0..n {
- // for (int i = 0; i < n; i++) {
- let x = x_values[i];
- let y = y_valuess[i];
- let denominator = self.a13 * x + self.a23 * y + self.a33;
- x_values[i] = (self.a11 * x + self.a21 * y + self.a31) / denominator;
- y_valuess[i] = (self.a12 * x + self.a22 * y + self.a32) / denominator;
- }
- }
-
- pub fn squareToQuadrilateral(
- x0: f32,
- y0: f32,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- x3: f32,
- y3: f32,
- ) -> Self {
- let dx3 = x0 - x1 + x2 - x3;
- let dy3 = y0 - y1 + y2 - y3;
- if dx3 == 0.0f32 && dy3 == 0.0f32 {
- // Affine
- return PerspectiveTransform::new(
- x1 - x0,
- x2 - x1,
- x0,
- y1 - y0,
- y2 - y1,
- y0,
- 0.0f32,
- 0.0f32,
- 1.0f32,
- );
- } else {
- let dx1 = x1 - x2;
- let dx2 = x3 - x2;
- let dy1 = y1 - y2;
- let dy2 = y3 - y2;
- let denominator = dx1 * dy2 - dx2 * dy1;
- let a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
- let a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
- return PerspectiveTransform::new(
- x1 - x0 + a13 * x1,
- x3 - x0 + a23 * x3,
- x0,
- y1 - y0 + a13 * y1,
- y3 - y0 + a23 * y3,
- y0,
- a13,
- a23,
- 1.0f32,
- );
- }
- }
-
- pub fn quadrilateralToSquare(
- x0: f32,
- y0: f32,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- x3: f32,
- y3: f32,
- ) -> Self {
- // Here, the adjoint serves as the inverse
- return PerspectiveTransform::squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)
- .buildAdjoint();
- }
-
- fn buildAdjoint(&self) -> Self {
- // Adjoint is the transpose of the cofactor matrix:
- return PerspectiveTransform::new(
- self.a22 * self.a33 - self.a23 * self.a32,
- self.a23 * self.a31 - self.a21 * self.a33,
- self.a21 * self.a32 - self.a22 * self.a31,
- self.a13 * self.a32 - self.a12 * self.a33,
- self.a11 * self.a33 - self.a13 * self.a31,
- self.a12 * self.a31 - self.a11 * self.a32,
- self.a12 * self.a23 - self.a13 * self.a22,
- self.a13 * self.a21 - self.a11 * self.a23,
- self.a11 * self.a22 - self.a12 * self.a21,
- );
- }
-
- fn times(&self, other: &Self) -> Self {
- return PerspectiveTransform::new(
- self.a11 * other.a11 + self.a21 * other.a12 + self.a31 * other.a13,
- self.a11 * other.a21 + self.a21 * other.a22 + self.a31 * other.a23,
- self.a11 * other.a31 + self.a21 * other.a32 + self.a31 * other.a33,
- self.a12 * other.a11 + self.a22 * other.a12 + self.a32 * other.a13,
- self.a12 * other.a21 + self.a22 * other.a22 + self.a32 * other.a23,
- self.a12 * other.a31 + self.a22 * other.a32 + self.a32 * other.a33,
- self.a13 * other.a11 + self.a23 * other.a12 + self.a33 * other.a13,
- self.a13 * other.a21 + self.a23 * other.a22 + self.a33 * other.a23,
- self.a13 * other.a31 + self.a23 * other.a32 + self.a33 * other.a33,
- );
- }
-}
-
-/*
- * 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.List;
-
-/**
- * Encapsulates the result of decoding a matrix of bits. This typically
- * applies to 2D barcode formats. For now it contains the raw bytes obtained,
- * as well as a String interpretation of those bytes, if applicable.
- *
- * @author Sean Owen
- */
-pub struct DecoderRXingResult {
- rawBytes: Vec,
- numBits: usize,
- text: String,
- byteSegments: Vec>,
- ecLevel: String,
- errorsCorrected: u64,
- erasures: u64,
- other: Box,
- structuredAppendParity: i32,
- structuredAppendSequenceNumber: i32,
- symbologyModifier: u32,
-}
-
-impl DecoderRXingResult {
- pub fn new(
- rawBytes: Vec,
- text: String,
- byteSegments: Vec>,
- ecLevel: String,
- ) -> Self {
- Self::with_all(rawBytes, text, byteSegments, ecLevel, -2, -2, 0)
- }
-
- pub fn with_symbology(
- rawBytes: Vec,
- text: String,
- byteSegments: Vec>,
- ecLevel: String,
- symbologyModifier: u32,
- ) -> Self {
- Self::with_all(
- rawBytes,
- text,
- byteSegments,
- ecLevel,
- -1,
- -1,
- symbologyModifier,
- )
- }
-
- pub fn with_sa(
- rawBytes: Vec,
- text: String,
- byteSegments: Vec>,
- ecLevel: String,
- saSequence: i32,
- saParity: i32,
- ) -> Self {
- Self::with_all(
- rawBytes,
- text,
- byteSegments,
- ecLevel,
- saSequence,
- saParity,
- 0,
- )
- }
-
- pub fn with_all(
- rawBytes: Vec,
- text: String,
- byteSegments: Vec>,
- ecLevel: String,
- saSequence: i32,
- saParity: i32,
- symbologyModifier: u32,
- ) -> Self {
- let nb = rawBytes.len();
- Self {
- rawBytes,
- numBits: nb,
- text,
- byteSegments,
- ecLevel,
- errorsCorrected: 0,
- erasures: 0,
- other: Box::new(false),
- structuredAppendParity: saParity,
- structuredAppendSequenceNumber: saSequence,
- symbologyModifier,
- }
- }
-
- /**
- * @return raw bytes representing the result, or {@code null} if not applicable
- */
- pub fn getRawBytes(&self) -> &Vec {
- &self.rawBytes
- }
-
- /**
- * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length
- * @since 3.3.0
- */
- pub fn getNumBits(&self) -> usize {
- self.numBits
- }
-
- /**
- * @param numBits overrides the number of bits that are valid in {@link #getRawBytes()}
- * @since 3.3.0
- */
- pub fn setNumBits(&mut self, numBits: usize) {
- self.numBits = numBits;
- }
-
- /**
- * @return text representation of the result
- */
- pub fn getText(&self) -> &str {
- &self.text
- }
-
- /**
- * @return list of byte segments in the result, or {@code null} if not applicable
- */
- pub fn getByteSegments(&self) -> &Vec> {
- &self.byteSegments
- }
-
- /**
- * @return name of error correction level used, or {@code null} if not applicable
- */
- pub fn getECLevel(&self) -> &str {
- &self.ecLevel
- }
-
- /**
- * @return number of errors corrected, or {@code null} if not applicable
- */
- pub fn getErrorsCorrected(&self) -> u64 {
- self.errorsCorrected
- }
-
- pub fn setErrorsCorrected(&mut self, errorsCorrected: u64) {
- self.errorsCorrected = errorsCorrected;
- }
-
- /**
- * @return number of erasures corrected, or {@code null} if not applicable
- */
- pub fn getErasures(&self) -> u64 {
- self.erasures
- }
-
- pub fn setErasures(&mut self, erasures: u64) {
- self.erasures = erasures
- }
-
- /**
- * @return arbitrary additional metadata
- */
- pub fn getOther(&self) -> &Box {
- &self.other
- }
-
- pub fn setOther(&mut self, other: Box) {
- self.other = other
- }
-
- pub fn hasStructuredAppend(&self) -> bool {
- self.structuredAppendParity >= 0 && self.structuredAppendSequenceNumber >= 0
- }
-
- pub fn getStructuredAppendParity(&self) -> i32 {
- self.structuredAppendParity
- }
-
- pub fn getStructuredAppendSequenceNumber(&self) -> i32 {
- self.structuredAppendSequenceNumber
- }
-
- pub fn getSymbologyModifier(&self) -> u32 {
- self.symbologyModifier
- }
-}
-
-/*
- * 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;
-
-// import java.io.ByteArrayOutputStream;
-
-/**
- * Class that lets one easily build an array of bytes by appending bits at a time.
- *
- * @author Sean Owen
- */
-pub struct BitSourceBuilder {
- output: Vec,
- nextByte: u32,
- bitsLeftInNextByte: u32,
-}
-
-impl BitSourceBuilder {
- pub fn new() -> Self {
- Self {
- output: Vec::new(),
- nextByte: 0,
- bitsLeftInNextByte: 8,
- }
- }
-
- pub fn write(&mut self, value: u32, numBits: u32) {
- if numBits <= self.bitsLeftInNextByte {
- self.nextByte <<= numBits;
- self.nextByte |= value;
- self.bitsLeftInNextByte -= numBits;
- if self.bitsLeftInNextByte == 0 {
- self.output.push(self.nextByte as u8);
- self.nextByte = 0;
- self.bitsLeftInNextByte = 8;
- }
- } else {
- let bitsToWriteNow = self.bitsLeftInNextByte;
- let numRestOfBits = numBits - bitsToWriteNow;
- let mask = 0xFF >> (8 - bitsToWriteNow);
- let valueToWriteNow = (value >> numRestOfBits) & mask;
- self.write(valueToWriteNow, bitsToWriteNow);
- self.write(value, numRestOfBits);
- }
- }
-
- pub fn toByteArray(&mut self) -> &Vec {
- if self.bitsLeftInNextByte < 8 {
- self.write(0, self.bitsLeftInNextByte);
- }
- &self.output
- }
-}
-
-/*
- * 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 com.google.zxing.NotFoundException;
-
-/**
- * Implementations of this class can, given locations of finder patterns for a QR code in an
- * image, sample the right points in the image to reconstruct the QR code, accounting for
- * perspective distortion. It is abstracted since it is relatively expensive and should be allowed
- * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
- * Imaging library, but which may not be available in other environments such as J2ME, and vice
- * versa.
- *
- * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
- * with an instance of a class which implements this interface.
- *
- * @author Sean Owen
- */
-
-pub trait GridSampler {
- // /**
- // * Sets the implementation of GridSampler used by the library. One global
- // * instance is stored, which may sound problematic. But, the implementation provided
- // * ought to be appropriate for the entire platform, and all uses of this library
- // * in the whole lifetime of the JVM. For instance, an Android activity can swap in
- // * an implementation that takes advantage of native platform libraries.
- // *
- // * @param newGridSampler The platform-specific object to install.
- // */
- // public static void setGridSampler(GridSampler newGridSampler) {
- // gridSampler = newGridSampler;
- // }
-
- // /**
- // * @return the current implementation of GridSampler
- // */
- // public static GridSampler getInstance() {
- // return gridSampler;
- // }
-
- /**
- * Samples an image for a rectangular matrix of bits of the given dimension. The sampling
- * transformation is determined by the coordinates of 4 points, in the original and transformed
- * image space.
- *
- * @param image image to sample
- * @param dimensionX width of {@link BitMatrix} to sample from image
- * @param dimensionY height of {@link BitMatrix} to sample from image
- * @param p1ToX point 1 preimage X
- * @param p1ToY point 1 preimage Y
- * @param p2ToX point 2 preimage X
- * @param p2ToY point 2 preimage Y
- * @param p3ToX point 3 preimage X
- * @param p3ToY point 3 preimage Y
- * @param p4ToX point 4 preimage X
- * @param p4ToY point 4 preimage Y
- * @param p1FromX point 1 image X
- * @param p1FromY point 1 image Y
- * @param p2FromX point 2 image X
- * @param p2FromY point 2 image Y
- * @param p3FromX point 3 image X
- * @param p3FromY point 3 image Y
- * @param p4FromX point 4 image X
- * @param p4FromY point 4 image Y
- * @return {@link BitMatrix} representing a grid of points sampled from the image within a region
- * defined by the "from" parameters
- * @throws NotFoundException if image can't be sampled, for example, if the transformation defined
- * by the given points is invalid or results in sampling outside the image boundaries
- */
- fn sample_grid_detailed(
- &self,
- image: &BitMatrix,
- dimensionX: u32,
- dimensionY: u32,
- p1ToX: f32,
- p1ToY: f32,
- p2ToX: f32,
- p2ToY: f32,
- p3ToX: f32,
- p3ToY: f32,
- p4ToX: f32,
- p4ToY: f32,
- p1FromX: f32,
- p1FromY: f32,
- p2FromX: f32,
- p2FromY: f32,
- p3FromX: f32,
- p3FromY: f32,
- p4FromX: f32,
- p4FromY: f32,
- ) -> Result;
-
- fn sample_grid(
- &self,
- image: &BitMatrix,
- dimensionX: u32,
- dimensionY: u32,
- transform: &PerspectiveTransform,
- ) -> Result;
-
- /**
- * Checks a set of points that have been transformed to sample points on an image against
- * the image's dimensions to see if the point are even within the image.
- *
- * This method will actually "nudge" the endpoints back onto the image if they are found to be
- * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
- * patterns in an image where the QR Code runs all the way to the image border.
- *
- * For efficiency, the method will check points from either end of the line until one is found
- * to be within the image. Because the set of points are assumed to be linear, this is valid.
- *
- * @param image image into which the points should map
- * @param points actual points in x1,y1,...,xn,yn form
- * @throws NotFoundException if an endpoint is lies outside the image boundaries
- */
- fn checkAndNudgePoints(&self, image: &BitMatrix, points: &mut [f32]) -> Result<(), Exceptions> {
- let width = image.getWidth();
- let height = image.getHeight();
- // Check and nudge points from start until we see some that are OK:
- let mut nudged = true;
- let max_offset = points.len() - 1; // points.length must be even
- let mut offset = 0;
- while offset < max_offset && nudged {
- // for (int offset = 0; offset < maxOffset && nudged; offset += 2) {
- let x = points[offset] as i32;
- let y = points[offset + 1] as i32;
- if x < -1 || x > width.try_into().unwrap() || y < -1 || y > height.try_into().unwrap() {
- return Err(Exceptions::NotFoundException(
- "getNotFoundInstance".to_owned(),
- ));
- }
- nudged = false;
- if x == -1 {
- points[offset] = 0.0f32;
- nudged = true;
- } else if x == width.try_into().unwrap() {
- points[offset] = width as f32 - 1f32;
- nudged = true;
- }
- if y == -1 {
- points[offset + 1] = 0.0f32;
- nudged = true;
- } else if (y == height.try_into().unwrap()) {
- points[offset + 1] = height as f32 - 1f32;
- nudged = true;
- }
- offset += 2;
- }
- // Check and nudge points from end:
- nudged = true;
- let mut offset = points.len() as isize - 2;
- while offset >= 0 && nudged {
- // for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) {
- let x = points[offset as usize] as i32;
- let y = points[offset as usize + 1] as i32;
- if x < -1 || x > width.try_into().unwrap() || y < -1 || y > height.try_into().unwrap() {
- return Err(Exceptions::NotFoundException(
- "getNotFoundInstance".to_owned(),
- ));
- }
- nudged = false;
- if x == -1 {
- points[offset as usize] = 0.0f32;
- nudged = true;
- } else if x == width.try_into().unwrap() {
- points[offset as usize] = width as f32 - 1f32;
- nudged = true;
- }
- if y == -1 {
- points[offset as usize + 1] = 0.0f32;
- nudged = true;
- } else if y == height.try_into().unwrap() {
- points[offset as usize + 1] = height as f32 - 1f32;
- nudged = true;
- }
- offset += -2;
- }
- Ok(())
- }
-}
-
-/*
- * 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 com.google.zxing.NotFoundException;
-
-/**
- * @author Sean Owen
- */
-pub struct DefaultGridSampler {}
-
-impl GridSampler for DefaultGridSampler {
- fn sample_grid_detailed(
- &self,
- image: &BitMatrix,
- dimensionX: u32,
- dimensionY: u32,
- p1ToX: f32,
- p1ToY: f32,
- p2ToX: f32,
- p2ToY: f32,
- p3ToX: f32,
- p3ToY: f32,
- p4ToX: f32,
- p4ToY: f32,
- p1FromX: f32,
- p1FromY: f32,
- p2FromX: f32,
- p2FromY: f32,
- p3FromX: f32,
- p3FromY: f32,
- p4FromX: f32,
- p4FromY: f32,
- ) -> Result {
- let transform = PerspectiveTransform::quadrilateralToQuadrilateral(
- p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX,
- p2FromY, p3FromX, p3FromY, p4FromX, p4FromY,
- );
-
- self.sample_grid(image, dimensionX, dimensionY, &transform)
- }
-
- fn sample_grid(
- &self,
- image: &BitMatrix,
- dimensionX: u32,
- dimensionY: u32,
- transform: &PerspectiveTransform,
- ) -> Result {
- if dimensionX == 0 || dimensionY == 0 {
- return Err(Exceptions::NotFoundException(
- "getNotFoundInstance".to_owned(),
- ));
- }
- let mut bits = BitMatrix::new(dimensionX, dimensionY)?;
- let mut points = vec![0_f32; 2 * dimensionX as usize];
- for y in 0..dimensionY {
- // for (int y = 0; y < dimensionY; y++) {
- let max = points.len();
- let i_value = y as f32 + 0.5f32;
- let mut x = 0;
- while x < max {
- // for (int x = 0; x < max; x += 2) {
- points[x] = (x as f32 / 2.0) + 0.5f32;
- points[x + 1] = i_value;
- x += 2;
- }
- transform.transform_points_single(&mut points);
- // Quick check to see if points transformed to something inside the image;
- // sufficient to check the endpoints
- self.checkAndNudgePoints(image, &mut points)?;
- // try {
- let mut x = 0;
- while x < max {
- // for (int x = 0; x < max; x += 2) {
- if points[x].floor() as u32 >= image.getWidth()
- || points[x + 1].floor() as u32 >= image.getHeight()
- {
- return Err(Exceptions::NotFoundException(
- "index out of bounds, see documentation in file for explanation".to_owned(),
- ));
- }
- if image.get(points[x].floor() as u32, points[x + 1].floor() as u32) {
- // Black(-ish) pixel
- bits.set(x as u32 / 2, y);
- }
- x += 2;
- }
- // } catch (ArrayIndexOutOfBoundsException aioobe) {
- // // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
- // // transform gets "twisted" such that it maps a straight line of points to a set of points
- // // whose endpoints are in bounds, but others are not. There is probably some mathematical
- // // way to detect this about the transformation that I don't know yet.
- // // This results in an ugly runtime exception despite our clever checks above -- can't have
- // // that. We could check each point's coordinates but that feels duplicative. We settle for
- // // catching and wrapping ArrayIndexOutOfBoundsException.
- // throw NotFoundException.getNotFoundInstance();
- // }
- }
- return Ok(bits);
- }
-}
-
-/*
- * 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;
-
-// import com.google.zxing.FormatException;
-
-// import java.nio.charset.Charset;
-
-// import java.util.HashMap;
-// import java.util.Map;
-
-/**
- * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
- * of ISO 18004.
- *
- * @author Sean Owen
- */
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum CharacterSetECI {
- // Enum name is a Java encoding valid for java.lang and java.io
- Cp437, //(new int[]{0,2}),
- ISO8859_1, //(new int[]{1,3}, "ISO-8859-1"),
- ISO8859_2, //(4, "ISO-8859-2"),
- ISO8859_3, //(5, "ISO-8859-3"),
- ISO8859_4, //(6, "ISO-8859-4"),
- ISO8859_5, //(7, "ISO-8859-5"),
- ISO8859_6, //(8, "ISO-8859-6"),
- ISO8859_7, //(9, "ISO-8859-7"),
- ISO8859_8, //(10, "ISO-8859-8"),
- ISO8859_9, //(11, "ISO-8859-9"),
- ISO8859_10, //(12, "ISO-8859-10"),
- ISO8859_11, //(13, "ISO-8859-11"),
- ISO8859_13, //(15, "ISO-8859-13"),
- ISO8859_14, //(16, "ISO-8859-14"),
- ISO8859_15, //(17, "ISO-8859-15"),
- ISO8859_16, //(18, "ISO-8859-16"),
- SJIS, //(20, "Shift_JIS"),
- Cp1250, //(21, "windows-1250"),
- Cp1251, //(22, "windows-1251"),
- Cp1252, //(23, "windows-1252"),
- Cp1256, //(24, "windows-1256"),
- UnicodeBigUnmarked, //(25, "UTF-16BE", "UnicodeBig"),
- UTF8, //(26, "UTF-8"),
- ASCII, //(new int[] {27, 170}, "US-ASCII"),
- Big5, //(28),
- GB18030, //(29, "GB2312", "EUC_CN", "GBK"),
- EUC_KR, //(30, "EUC-KR");
-}
-impl CharacterSetECI {
- // private static final Map VALUE_TO_ECI = new HashMap<>();
- // private static final Map NAME_TO_ECI = new HashMap<>();
- // static {
- // for (CharacterSetECI eci : values()) {
- // for (int value : eci.values) {
- // VALUE_TO_ECI.put(value, eci);
- // }
- // NAME_TO_ECI.put(eci.name(), eci);
- // for (String name : eci.otherEncodingNames) {
- // NAME_TO_ECI.put(name, eci);
- // }
- // }
- // }
-
- // private final int[] values;
- // private final String[] otherEncodingNames;
-
- // CharacterSetECI(int value) {
- // this(new int[] {value});
- // }
-
- // CharacterSetECI(int value, String... otherEncodingNames) {
- // this.values = new int[] {value};
- // this.otherEncodingNames = otherEncodingNames;
- // }
-
- // CharacterSetECI(int[] values, String... otherEncodingNames) {
- // this.values = values;
- // this.otherEncodingNames = otherEncodingNames;
- // }
-
- pub fn getValueSelf(&self) -> u32 {
- Self::getValue(self)
- }
-
- pub fn getValue(cs_eci: &CharacterSetECI) -> u32 {
- match cs_eci {
- CharacterSetECI::Cp437 => 0,
- CharacterSetECI::ISO8859_1 => 1,
- CharacterSetECI::ISO8859_2 => 4,
- CharacterSetECI::ISO8859_3 => 5,
- CharacterSetECI::ISO8859_4 => 6,
- CharacterSetECI::ISO8859_5 => 7,
- CharacterSetECI::ISO8859_6 => 8,
- CharacterSetECI::ISO8859_7 => 9,
- CharacterSetECI::ISO8859_8 => 10,
- CharacterSetECI::ISO8859_9 => 11,
- CharacterSetECI::ISO8859_10 => 12,
- CharacterSetECI::ISO8859_11 => 13,
- CharacterSetECI::ISO8859_13 => 15,
- CharacterSetECI::ISO8859_14 => 16,
- CharacterSetECI::ISO8859_15 => 17,
- CharacterSetECI::ISO8859_16 => 18,
- CharacterSetECI::SJIS => 20,
- CharacterSetECI::Cp1250 => 21,
- CharacterSetECI::Cp1251 => 22,
- CharacterSetECI::Cp1252 => 23,
- CharacterSetECI::Cp1256 => 24,
- CharacterSetECI::UnicodeBigUnmarked => 25,
- CharacterSetECI::UTF8 => 26,
- CharacterSetECI::ASCII => 27,
- CharacterSetECI::Big5 => 28,
- CharacterSetECI::GB18030 => 29,
- CharacterSetECI::EUC_KR => 30,
- }
- }
-
- pub fn getCharset(cs_eci: &CharacterSetECI) -> EncodingRef {
- let name = match cs_eci {
- // CharacterSetECI::Cp437 => "CP437",
- CharacterSetECI::Cp437 => "UTF-8",
- CharacterSetECI::ISO8859_1 => "ISO-8859-1",
- CharacterSetECI::ISO8859_2 => "ISO-8859-2",
- CharacterSetECI::ISO8859_3 => "ISO-8859-3",
- CharacterSetECI::ISO8859_4 => "ISO-8859-4",
- CharacterSetECI::ISO8859_5 => "ISO-8859-5",
- CharacterSetECI::ISO8859_6 => "ISO-8859-6",
- CharacterSetECI::ISO8859_7 => "ISO-8859-7",
- CharacterSetECI::ISO8859_8 => "ISO-8859-8",
- CharacterSetECI::ISO8859_9 => "ISO-8859-9",
- CharacterSetECI::ISO8859_10 => "ISO-8859-10",
- CharacterSetECI::ISO8859_11 => "ISO-8859-11",
- CharacterSetECI::ISO8859_13 => "ISO-8859-13",
- CharacterSetECI::ISO8859_14 => "ISO-8859-14",
- CharacterSetECI::ISO8859_15 => "ISO-8859-15",
- CharacterSetECI::ISO8859_16 => "ISO-8859-16",
- CharacterSetECI::SJIS => "Shift_JIS",
- CharacterSetECI::Cp1250 => "windows-1250",
- CharacterSetECI::Cp1251 => "windows-1251",
- CharacterSetECI::Cp1252 => "windows-1252",
- CharacterSetECI::Cp1256 => "windows-1256",
- CharacterSetECI::UnicodeBigUnmarked => "UTF-16BE",
- CharacterSetECI::UTF8 => "UTF-8",
- CharacterSetECI::ASCII => "US-ASCII",
- CharacterSetECI::Big5 => "Big5",
- CharacterSetECI::GB18030 => "GB2312",
- CharacterSetECI::EUC_KR => "EUC-KR",
- };
- encoding::label::encoding_from_whatwg_label(name).unwrap()
- }
-
- /**
- * @param charset Java character set object
- * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
- * but unsupported
- */
- pub fn getCharacterSetECI(charset: EncodingRef) -> Option {
- let name = if let Some(nm) = charset.whatwg_name() {
- nm
- } else {
- charset.name()
- };
- match name {
- "CP437" => Some(CharacterSetECI::Cp437),
- "iso-8859-1" => Some(CharacterSetECI::ISO8859_1),
- "iso-8859-2" => Some(CharacterSetECI::ISO8859_2),
- "iso-8859-3" => Some(CharacterSetECI::ISO8859_3),
- "iso-8859-4" => Some(CharacterSetECI::ISO8859_4),
- "iso-8859-5" => Some(CharacterSetECI::ISO8859_5),
- "iso-8859-6" => Some(CharacterSetECI::ISO8859_6),
- "iso-8859-7" => Some(CharacterSetECI::ISO8859_7),
- "iso-8859-8" => Some(CharacterSetECI::ISO8859_8),
- "iso-8859-9" => Some(CharacterSetECI::ISO8859_9),
- "iso-8859-10" => Some(CharacterSetECI::ISO8859_10),
- "iso-8859-11" => Some(CharacterSetECI::ISO8859_11),
- "iso-8859-13" => Some(CharacterSetECI::ISO8859_13),
- "iso-8859-14" => Some(CharacterSetECI::ISO8859_14),
- "iso-8859-15" => Some(CharacterSetECI::ISO8859_15),
- "iso-8859-16" => Some(CharacterSetECI::ISO8859_16),
- "shift_jis" => Some(CharacterSetECI::SJIS),
- "windows-1250" => Some(CharacterSetECI::Cp1250),
- "windows-1251" => Some(CharacterSetECI::Cp1251),
- "windows-1252" => Some(CharacterSetECI::Cp1252),
- "windows-1256" => Some(CharacterSetECI::Cp1256),
- "utf-16be" => Some(CharacterSetECI::UnicodeBigUnmarked),
- "utf-8" => Some(CharacterSetECI::UTF8),
- "us-ascii" => Some(CharacterSetECI::ASCII),
- "big5" => Some(CharacterSetECI::Big5),
- "gb2312" => Some(CharacterSetECI::GB18030),
- "euc-kr" => Some(CharacterSetECI::EUC_KR),
- _ => None,
- }
- }
-
- /**
- * @param value character set ECI value
- * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but
- * unsupported
- * @throws FormatException if ECI value is invalid
- */
- pub fn getCharacterSetECIByValue(value: u32) -> Result {
- match value {
- 0 | 2 => Ok(CharacterSetECI::Cp437),
- 1 | 3 => Ok(CharacterSetECI::ISO8859_1),
- 4 => Ok(CharacterSetECI::ISO8859_2),
- 5 => Ok(CharacterSetECI::ISO8859_3),
- 6 => Ok(CharacterSetECI::ISO8859_4),
- 7 => Ok(CharacterSetECI::ISO8859_5),
- 8 => Ok(CharacterSetECI::ISO8859_6),
- 9 => Ok(CharacterSetECI::ISO8859_7),
- 10 => Ok(CharacterSetECI::ISO8859_8),
- 11 => Ok(CharacterSetECI::ISO8859_9),
- 12 => Ok(CharacterSetECI::ISO8859_10),
- 13 => Ok(CharacterSetECI::ISO8859_11),
- 15 => Ok(CharacterSetECI::ISO8859_13),
- 16 => Ok(CharacterSetECI::ISO8859_14),
- 17 => Ok(CharacterSetECI::ISO8859_15),
- 18 => Ok(CharacterSetECI::ISO8859_16),
- 20 => Ok(CharacterSetECI::SJIS),
- 21 => Ok(CharacterSetECI::Cp1250),
- 22 => Ok(CharacterSetECI::Cp1251),
- 23 => Ok(CharacterSetECI::Cp1252),
- 24 => Ok(CharacterSetECI::Cp1256),
- 25 => Ok(CharacterSetECI::UnicodeBigUnmarked),
- 26 => Ok(CharacterSetECI::UTF8),
- 27 | 170 => Ok(CharacterSetECI::ASCII),
- 28 => Ok(CharacterSetECI::Big5),
- 29 => Ok(CharacterSetECI::GB18030),
- 30 => Ok(CharacterSetECI::EUC_KR),
- _ => Err(Exceptions::NotFoundException("Bad ECI Value".to_owned())),
- }
- }
-
- /**
- * @param name character set ECI encoding name
- * @return CharacterSetECI representing ECI for character encoding, or null if it is legal
- * but unsupported
- */
- pub fn getCharacterSetECIByName(name: &str) -> Option {
- match name {
- "CP437" => Some(CharacterSetECI::Cp437),
- "ISO-8859-1" => Some(CharacterSetECI::ISO8859_1),
- "ISO-8859-2" => Some(CharacterSetECI::ISO8859_2),
- "ISO-8859-3" => Some(CharacterSetECI::ISO8859_3),
- "ISO-8859-4" => Some(CharacterSetECI::ISO8859_4),
- "ISO-8859-5" => Some(CharacterSetECI::ISO8859_5),
- "ISO-8859-6" => Some(CharacterSetECI::ISO8859_6),
- "ISO-8859-7" => Some(CharacterSetECI::ISO8859_7),
- "ISO-8859-8" => Some(CharacterSetECI::ISO8859_8),
- "ISO-8859-9" => Some(CharacterSetECI::ISO8859_9),
- "ISO-8859-10" => Some(CharacterSetECI::ISO8859_10),
- "ISO-8859-11" => Some(CharacterSetECI::ISO8859_11),
- "ISO-8859-13" => Some(CharacterSetECI::ISO8859_13),
- "ISO-8859-14" => Some(CharacterSetECI::ISO8859_14),
- "ISO-8859-15" => Some(CharacterSetECI::ISO8859_15),
- "ISO-8859-16" => Some(CharacterSetECI::ISO8859_16),
- "Shift_JIS" => Some(CharacterSetECI::SJIS),
- "windows-1250" => Some(CharacterSetECI::Cp1250),
- "windows-1251" => Some(CharacterSetECI::Cp1251),
- "windows-1252" => Some(CharacterSetECI::Cp1252),
- "windows-1256" => Some(CharacterSetECI::Cp1256),
- "UTF-16BE" => Some(CharacterSetECI::UnicodeBigUnmarked),
- "UTF-8" => Some(CharacterSetECI::UTF8),
- "US-ASCII" => Some(CharacterSetECI::ASCII),
- "Big5" => Some(CharacterSetECI::Big5),
- "GB2312" => Some(CharacterSetECI::GB18030),
- "EUC-KR" => Some(CharacterSetECI::EUC_KR),
- _ => None,
- }
- }
-}
-
-/*
- * Copyright 2022 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 com.google.zxing.FormatException;
-
-// import java.nio.charset.Charset;
-// import java.nio.charset.StandardCharsets;
-
-/**
- * Class that converts a sequence of ECIs and bytes into a string
- *
- * @author Alex Geller
- */
-pub struct ECIStringBuilder {
- current_bytes: Vec,
- result: String,
- current_charset: EncodingRef, //= StandardCharsets.ISO_8859_1;
-}
-
-impl ECIStringBuilder {
- pub fn new() -> Self {
- Self {
- current_bytes: Vec::new(),
- result: String::new(),
- current_charset: encoding::all::UTF_8,
- }
- }
- pub fn with_capacity(initial_capacity: usize) -> Self {
- Self {
- current_bytes: Vec::with_capacity(initial_capacity),
- result: String::new(),
- current_charset: encoding::all::ISO_8859_1,
- }
- }
-
- /**
- * Appends {@code value} as a byte value
- *
- * @param value character whose lowest byte is to be appended
- */
- pub fn append_char(&mut self, value: char) {
- self.current_bytes.push(value as u8);
- }
-
- /**
- * Appends {@code value} as a byte value
- *
- * @param value byte to append
- */
- pub fn append_byte(&mut self, value: u8) {
- self.current_bytes.push(value);
- }
-
- /**
- * Appends the characters in {@code value} as bytes values
- *
- * @param value string to append
- */
- pub fn append_string(&mut self, value: &str) {
- value.as_bytes().iter().map(|b| self.current_bytes.push(*b));
- // self.current_bytes.push(value.as_bytes());
- }
-
- /**
- * Append the string repesentation of {@code value} (short for {@code append(String.valueOf(value))})
- *
- * @param value int to append as a string
- */
- pub fn append(&mut self, value: i32) {
- self.append_string(&format!("{}", value));
- }
-
- /**
- * Appends ECI value to output.
- *
- * @param value ECI value to append, as an int
- * @throws FormatException on invalid ECI value
- */
- pub fn appendECI(&mut self, value: u32) -> Result<(), Exceptions> {
- self.encodeCurrentBytesIfAny();
- let character_set_eci = CharacterSetECI::getCharacterSetECIByValue(value)?;
- // if (character_set_eci == null) {
- // throw FormatException.getFormatInstance();
- // }
- self.current_charset = CharacterSetECI::getCharset(&character_set_eci);
- Ok(())
- }
-
- pub fn encodeCurrentBytesIfAny(&mut self) {
- if self.current_charset.name() == encoding::all::UTF_8.name() {
- if !self.current_bytes.is_empty() {
- // if result == null {
- // result = currentBytes;
- // currentBytes = new StringBuilder();
- // } else {
- self.result
- .push_str(&String::from_utf8(self.current_bytes.clone()).unwrap());
- self.current_bytes.clear();
- // }
- }
- } else if !self.current_bytes.is_empty() {
- let bytes = self.current_bytes.clone();
- self.current_bytes.clear();
- // if (result == null) {
- // result = new StringBuilder(new String(bytes, currentCharset));
- // } else {
- let encoded_value = self
- .current_charset
- .decode(&bytes, encoding::DecoderTrap::Replace)
- .unwrap();
- self.result.push_str(&encoded_value);
- // }
- }
- }
-
- /**
- * Appends the characters from {@code value} (unlike all other append methods of this class who append bytes)
- *
- * @param value characters to append
- */
- pub fn appendCharacters(&mut self, value: &str) {
- self.encodeCurrentBytesIfAny();
- self.result.push_str(value);
- }
-
- /**
- * Short for {@code toString().length()} (if possible, use {@link #isEmpty()} instead)
- *
- * @return length of string representation in characters
- */
- pub fn len(&mut self) -> usize {
- self.encodeCurrentBytesIfAny(); //return toString().length();
- self.result.len()
- }
-
- /**
- * @return true iff nothing has been appended
- */
- pub fn is_empty(&self) -> bool {
- return self.current_bytes.is_empty() && self.result.is_empty();
- }
-}
-
-impl fmt::Display for ECIStringBuilder {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- //self.encodeCurrentBytesIfAny();
- write!(f, "{}", self.result)
- }
-}
-
-/*
- * Copyright 2021 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.nio.charset.Charset;
-// import java.nio.charset.CharsetEncoder;
-// import java.nio.charset.StandardCharsets;
-// import java.nio.charset.UnsupportedCharsetException;
-// import java.util.ArrayList;
-// import java.util.List;
-
-lazy_static! {
- static ref ENCODERS : Vec = {
- let mut enc_vec = Vec::new();
- for name in NAMES {
- if let Some(enc) = CharacterSetECI::getCharacterSetECIByName(name) {
- // try {
- enc_vec.push(CharacterSetECI::getCharset(&enc));
- // } catch (UnsupportedCharsetException e) {
- // continue
- // }
- }
- }
- enc_vec
- };
-}
-const NAMES: [&str; 20] = [
- "IBM437",
- "ISO-8859-2",
- "ISO-8859-3",
- "ISO-8859-4",
- "ISO-8859-5",
- "ISO-8859-6",
- "ISO-8859-7",
- "ISO-8859-8",
- "ISO-8859-9",
- "ISO-8859-10",
- "ISO-8859-11",
- "ISO-8859-13",
- "ISO-8859-14",
- "ISO-8859-15",
- "ISO-8859-16",
- "windows-1250",
- "windows-1251",
- "windows-1252",
- "windows-1256",
- "Shift_JIS",
-];
-
-/**
- * Set of CharsetEncoders for a given input string
- *
- * Invariants:
- * - The list contains only encoders from CharacterSetECI (list is shorter then the list of encoders available on
- * the platform for which ECI values are defined).
- * - The list contains encoders at least one encoder for every character in the input.
- * - The first encoder in the list is always the ISO-8859-1 encoder even of no character in the input can be encoded
- * by it.
- * - If the input contains a character that is not in ISO-8859-1 then the last two entries in the list will be the
- * UTF-8 encoder and the UTF-16BE encoder.
- *
- * @author Alex Geller
- */
-#[derive(Clone)]
-pub struct ECIEncoderSet {
- encoders: Vec,
- priorityEncoderIndex: Option,
-}
-
-impl ECIEncoderSet {
- /**
- * Constructs an encoder set
- *
- * @param stringToEncode the string that needs to be encoded
- * @param priorityCharset The preferred {@link Charset} or null.
- * @param fnc1 fnc1 denotes the character in the input that represents the FNC1 character or -1 for a non-GS1 bar
- * code. When specified, it is considered an error to pass it as argument to the methods canEncode() or encode().
- */
- pub fn new(
- stringToEncodeMain: &str,
- priorityCharset: Option,
- fnc1: Option<&str>,
- ) -> Self {
- // List of encoders that potentially encode characters not in ISO-8859-1 in one byte.
-
- let mut encoders: Vec;
- let mut priorityEncoderIndexValue = None;
-
- let mut neededEncoders: Vec = Vec::new();
-
- let stringToEncode = stringToEncodeMain.graphemes(true).collect::>();
-
- //we always need the ISO-8859-1 encoder. It is the default encoding
- neededEncoders.push(encoding::all::ISO_8859_1);
- let mut needUnicodeEncoder = if let Some(pc) = priorityCharset {
- pc.name().starts_with("UTF")
- } else {
- false
- };
-
- //Walk over the input string and see if all characters can be encoded with the list of encoders
- for i in 0..stringToEncode.len() {
- // for (int i = 0; i < stringToEncode.length(); i++) {
- let mut canEncode = false;
- for encoder in &neededEncoders {
- // for (CharsetEncoder encoder : neededEncoders) {
- let c = stringToEncode.get(i).unwrap();
- if (fnc1.is_some() && c == fnc1.as_ref().unwrap())
- || encoder.encode(c, encoding::EncoderTrap::Strict).is_ok()
- {
- canEncode = true;
- break;
- }
- }
- if !canEncode {
- //for the character at position i we don't yet have an encoder in the list
- for i_encoder in 0..ENCODERS.len() {
- // for encoder in ENCODERS {
- let encoder = ENCODERS.get(i_encoder).unwrap();
- // for (CharsetEncoder encoder : ENCODERS) {
- if encoder
- .encode(
- &stringToEncode.get(i).unwrap(),
- encoding::EncoderTrap::Strict,
- )
- .is_ok()
- {
- //Good, we found an encoder that can encode the character. We add him to the list and continue scanning
- //the input
- neededEncoders.push(*encoder);
- canEncode = true;
- break;
- }
- }
- }
-
- if !canEncode {
- //The character is not encodeable by any of the single byte encoders so we remember that we will need a
- //Unicode encoder.
- needUnicodeEncoder = true;
- }
- }
-
- if neededEncoders.len() == 1 && !needUnicodeEncoder {
- //the entire input can be encoded by the ISO-8859-1 encoder
- encoders = vec![encoding::all::ISO_8859_1];
- } else {
- // we need more than one single byte encoder or we need a Unicode encoder.
- // In this case we append a UTF-8 and UTF-16 encoder to the list
- // encoders = [] new CharsetEncoder[neededEncoders.size() + 2];
- encoders = Vec::new();
- let index = 0;
-
- for encoder in neededEncoders {
- // for (CharsetEncoder encoder : neededEncoders) {
- //encoders[index++] = encoder;
- encoders.push(encoder);
- }
-
- encoders.push(encoding::all::UTF_8);
- encoders.push(encoding::all::UTF_16BE);
- }
-
- //Compute priorityEncoderIndex by looking up priorityCharset in encoders
- // if priorityCharset != null {
- if priorityCharset.is_some() {
- for i in 0..encoders.len() {
- // for (int i = 0; i < encoders.length; i++) {
- if priorityCharset.as_ref().unwrap().name() == encoders[i].name() {
- priorityEncoderIndexValue = Some(i);
- break;
- }
- }
- }
- // }
- //invariants
- assert_eq!(encoders[0].name(), encoding::all::ISO_8859_1.name());
- Self {
- encoders: encoders,
- priorityEncoderIndex: priorityEncoderIndexValue,
- }
- }
-
- pub fn len(&self) -> usize {
- return self.encoders.len();
- }
-
- pub fn getCharsetName(&self, index: usize) -> &'static str {
- assert!(index < self.len());
- return self.encoders[index].name();
- }
-
- pub fn getCharset(&self, index: usize) -> EncodingRef {
- assert!(index < self.len());
- return self.encoders[index];
- }
-
- pub fn getECIValue(&self, encoderIndex: usize) -> u32 {
- CharacterSetECI::getValue(
- &CharacterSetECI::getCharacterSetECI(self.encoders[encoderIndex]).unwrap(),
- )
- }
-
- /*
- * returns -1 if no priority charset was defined
- */
- pub fn getPriorityEncoderIndex(&self) -> Option {
- self.priorityEncoderIndex
- }
-
- pub fn canEncode(&self, c: &str, encoderIndex: usize) -> bool {
- assert!(encoderIndex < self.len());
- let encoder = self.encoders[encoderIndex];
- let enc_data = encoder.encode(c, encoding::EncoderTrap::Strict);
-
- enc_data.is_ok()
- }
-
- pub fn encode_char(&self, c: &str, encoderIndex: usize) -> Vec {
- assert!(encoderIndex < self.len());
- let encoder = self.encoders[encoderIndex];
- let enc_data = encoder.encode(&c.to_string(), encoding::EncoderTrap::Strict);
- assert!(enc_data.is_ok());
- return enc_data.unwrap();
- }
-
- pub fn encode_string(&self, s: &str, encoderIndex: usize) -> Vec {
- assert!(encoderIndex < self.len());
- let encoder = self.encoders[encoderIndex];
- encoder.encode(s, encoding::EncoderTrap::Replace).unwrap()
- }
-}
-
-/*
- * Copyright 2021 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.nio.charset.Charset;
-// import java.util.ArrayList;
-// import java.util.List;
-
-//* approximated (latch + 2 codewords)
-static COST_PER_ECI: usize = 3;
-
-/**
- * Class that converts a character string into a sequence of ECIs and bytes
- *
- * The implementation uses the Dijkstra algorithm to produce minimal encodings
- *
- * @author Alex Geller
- */
-pub struct MinimalECIInput {
- bytes: Vec,
- fnc1: u16,
-}
-
-impl ECIInput for MinimalECIInput {
- /**
- * Returns the length of this input. The length is the number
- * of {@code byte}s, FNC1 characters or ECIs in the sequence.
- *
- * @return the number of {@code char}s in this sequence
- */
- fn length(&self) -> usize {
- return self.bytes.len();
- }
-
- /**
- * Returns the {@code byte} value at the specified index. An index ranges from zero
- * to {@code length() - 1}. The first {@code byte} value of the sequence is at
- * index zero, the next at index one, and so on, as for array
- * indexing.
- *
- * @param index the index of the {@code byte} value to be returned
- *
- * @return the specified {@code byte} value as character or the FNC1 character
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- * @throws IllegalArgumentException
- * if the value at the {@code index} argument is an ECI (@see #isECI)
- */
- fn charAt(&self, index: usize) -> Result {
- if index >= self.length() {
- return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
- }
- if self.isECI(index as u32)? {
- return Err(Exceptions::IllegalArgumentException(format!(
- "value at {} is not a character but an ECI",
- index
- )));
- }
- if self.isFNC1(index)? {
- Ok(self.fnc1 as u8 as char)
- } else {
- Ok(self.bytes[index] as u8 as char)
- }
- }
-
- /**
- * Returns a {@code CharSequence} that is a subsequence of this sequence.
- * The subsequence starts with the {@code char} value at the specified index and
- * ends with the {@code char} value at index {@code end - 1}. The length
- * (in {@code char}s) of the
- * returned sequence is {@code end - start}, so if {@code start == end}
- * then an empty sequence is returned.
- *
- * @param start the start index, inclusive
- * @param end the end index, exclusive
- *
- * @return the specified subsequence
- *
- * @throws IndexOutOfBoundsException
- * if {@code start} or {@code end} are negative,
- * if {@code end} is greater than {@code length()},
- * or if {@code start} is greater than {@code end}
- * @throws IllegalArgumentException
- * if a value in the range {@code start}-{@code end} is an ECI (@see #isECI)
- */
- fn subSequence(&self, start: usize, end: usize) -> Result, Exceptions> {
- if start > end || end > self.length() {
- return Err(Exceptions::IndexOutOfBoundsException(start.to_string()));
- }
- let mut result = String::new();
- for i in start..end {
- // for (int i = start; i < end; i++) {
- if self.isECI(i as u32)? {
- return Err(Exceptions::IllegalArgumentException(format!(
- "value at {} is not a character but an ECI",
- i
- )));
- }
- result.push_str(&self.charAt(i)?.to_string());
- }
- Ok(result.chars().collect())
- }
-
- /**
- * Determines if a value is an ECI
- *
- * @param index the index of the value
- *
- * @return true if the value at position {@code index} is an ECI
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- */
- fn isECI(&self, index: u32) -> Result {
- if index >= self.length() as u32 {
- return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
- }
- Ok(self.bytes[index as usize] > 255)// && self.bytes[index as usize] <= u16::MAX)
- }
-
- /**
- * Returns the {@code int} ECI value at the specified index. An index ranges from zero
- * to {@code length() - 1}. The first {@code byte} value of the sequence is at
- * index zero, the next at index one, and so on, as for array
- * indexing.
- *
- * @param index the index of the {@code int} value to be returned
- *
- * @return the specified {@code int} ECI value.
- * The ECI specified the encoding of all bytes with a higher index until the
- * next ECI or until the end of the input if no other ECI follows.
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- * @throws IllegalArgumentException
- * if the value at the {@code index} argument is not an ECI (@see #isECI)
- */
- fn getECIValue(&self, index: usize) -> Result {
- if index >= self.length() {
- return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
- }
- if !self.isECI(index as u32)? {
- return Err(Exceptions::IllegalArgumentException(format!(
- "value at {} is not an ECI but a character",
- index
- )));
- }
- Ok((self.bytes[index] as u32 - 256) as u32)
- }
-
- fn haveNCharacters(&self, index: usize, n: usize) -> bool {
- if index + n - 1 >= self.bytes.len() {
- return false;
- }
- for i in 0..n {
- // for (int i = 0; i < n; i++) {
- if self.isECI(index as u32 + i as u32).unwrap() {
- return false;
- }
- }
- return true;
- }
-}
-impl MinimalECIInput {
- /**
- * Constructs a minimal input
- *
- * @param stringToEncode the character string to encode
- * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
- * chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
- * charset to encode any character in the input that can be encoded by it if the charset is among the
- * supported charsets.
- * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not GS1
- * input.
- */
- pub fn new(
- stringToEncodeInput: &str,
- priorityCharset: Option,
- fnc1: Option<&str>,
- ) -> Self {
- let stringToEncode = stringToEncodeInput.graphemes(true).collect::>();
- let encoderSet = ECIEncoderSet::new(stringToEncodeInput, priorityCharset, fnc1);
- let bytes = if encoderSet.len() == 1 {
- //optimization for the case when all can be encoded without ECI in ISO-8859-1
- let mut bytes_hld = vec![0; stringToEncode.len()];
- for i in 0..stringToEncode.len() {
- // for (int i = 0; i < bytes.length; i++) {
- let c = stringToEncode.get(i).unwrap();
- bytes_hld[i] = if fnc1.is_some() && c == fnc1.as_ref().unwrap() {
- 1000
- } else {
- c.chars().nth(0).unwrap() as u16
- };
- }
- bytes_hld
- } else {
- Self::encodeMinimally(
- stringToEncodeInput,
- &encoderSet,
- fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16,
- )
- };
-
- Self {
- bytes: bytes,
- fnc1: fnc1.as_ref().unwrap().chars().nth(0).unwrap() as u16,
- }
- }
-
- pub fn getFNC1Character(&self) -> u16 {
- self.fnc1
- }
-
- /**
- * Determines if a value is the FNC1 character
- *
- * @param index the index of the value
- *
- * @return true if the value at position {@code index} is the FNC1 character
- *
- * @throws IndexOutOfBoundsException
- * if the {@code index} argument is negative or not less than
- * {@code length()}
- */
- pub fn isFNC1(&self, index: usize) -> Result {
- if index >= self.length() {
- return Err(Exceptions::IndexOutOfBoundsException(index.to_string()));
- }
- Ok(self.bytes[index] == 1000)
- }
-
- fn addEdge(edges: &mut Vec>>>, to: usize, edge: Rc) {
- if edges[to][edge.encoderIndex].is_none()
- || edges[to][edge.encoderIndex]
- .clone()
- .unwrap()
- .cachedTotalSize
- > edge.cachedTotalSize
- {
- edges[to][edge.encoderIndex] = Some(edge.clone());
- }
- }
-
- fn addEdges(
- stringToEncode: &str,
- encoderSet: &ECIEncoderSet,
- edges: &mut Vec>>>,
- from: usize,
- previous: Option>,
- fnc1: u16,
- ) {
- // let ch = stringToEncode.chars().nth(from).unwrap() as i16;
- let ch = stringToEncode.graphemes(true).nth(from).unwrap();
-
- let mut start = 0;
- let mut end = encoderSet.len();
- if encoderSet.getPriorityEncoderIndex().is_some()
- && (ch.chars().nth(0).unwrap() as u16 == fnc1
- || encoderSet.canEncode(ch, encoderSet.getPriorityEncoderIndex().unwrap()))
- {
- start = encoderSet.getPriorityEncoderIndex().unwrap();
- end = start + 1;
- }
-
- for i in start..end {
- // for (int i = start; i < end; i++) {
- if ch.chars().nth(0).unwrap() as u16 == fnc1 || encoderSet.canEncode(ch, i) {
- Self::addEdge(
- edges,
- from + 1,
- Rc::new(InputEdge::new(ch, encoderSet, i, previous.clone(), fnc1)),
- );
- }
- }
- }
-
- pub fn encodeMinimally(
- stringToEncode: &str,
- encoderSet: &ECIEncoderSet,
- fnc1: u16,
- ) -> Vec {
- let inputLength = stringToEncode.len();
-
- // Array that represents vertices. There is a vertex for every character and encoding.
- let mut edges = vec![vec![None; encoderSet.len()]; inputLength + 1]; //InputEdge[inputLength + 1][encoderSet.length()];
- Self::addEdges(stringToEncode, encoderSet, &mut edges, 0, None, fnc1);
-
- for i in 0..=inputLength {
- // for (int i = 1; i <= inputLength; i++) {
- for j in 0..encoderSet.len() {
- // for (int j = 0; j < encoderSet.length(); j++) {
- if edges[i][j].is_some() && i < inputLength {
- let edg = edges[i][j].clone();
- Self::addEdges(stringToEncode, encoderSet, &mut edges, i, edg, fnc1);
- }
- }
- //optimize memory by removing edges that have been passed.
- for j in 0..encoderSet.len() {
- // for (int j = 0; j < encoderSet.length(); j++) {
- edges[i - 1][j] = None;
- }
- }
- let mut minimalJ: i32 = -1;
- let mut minimalSize: i32 = i32::MAX;
- for j in 0..encoderSet.len() {
- // for (int j = 0; j < encoderSet.length(); j++) {
- if edges[inputLength][j].is_some() {
- let edge = edges[inputLength][j].clone().unwrap();
- if (edge.cachedTotalSize as i32) < minimalSize {
- minimalSize = edge.cachedTotalSize as i32;
- minimalJ = j as i32;
- }
- }
- }
- if minimalJ < 0 {
- panic!("Internal error: failed to encode \"{}\"", stringToEncode);
- }
- let mut intsAL: Vec = Vec::new();
- let mut current = edges[inputLength][minimalJ as usize].clone();
- while current.is_some() {
- let c = current.unwrap().clone();
- if c.isFNC1() {
- intsAL.splice(0..0, [1000]);
- } else {
- let bytes: Vec = encoderSet
- .encode_char(&c.c, c.encoderIndex)
- .iter()
- .map(|x| *x as u16)
- .collect();
- let mut i = bytes.len() as i32 - 1;
- while i >= 0 {
- // for (int i = bytes.length - 1; i >= 0; i--) {
- intsAL.splice(0..0, [bytes[i as usize]]);
- i = -1;
- }
- }
- let previousEncoderIndex = if c.previous.is_none() {
- 0
- } else {
- c.previous.clone().unwrap().encoderIndex
- };
- if previousEncoderIndex != c.encoderIndex {
- intsAL.splice(
- 0..0,
- [256 as u16 + encoderSet.getECIValue(c.encoderIndex) as u16],
- );
- }
- current = c.previous.clone();
- }
- let mut ints = vec![0; intsAL.len()];
- for i in 0..ints.len() {
- // for (int i = 0; i < ints.length; i++) {
- ints[i] = *intsAL.get(i).unwrap() as u16;
- }
- return ints;
- }
-}
-
-struct InputEdge {
- c: String,
- encoderIndex: usize, //the encoding of this edge
- previous: Option>,
- cachedTotalSize: usize,
-}
-impl InputEdge {
- pub fn new(
- c: &str,
- encoderSet: &ECIEncoderSet,
- encoderIndex: usize,
- previous: Option>,
- fnc1: u16,
- ) -> Self {
- let mut size = if c == "\u{1000}" {
- 1
- } else {
- encoderSet.encode_char(c, encoderIndex).len()
- };
-
- let fnc1Str = String::from_utf16(&[fnc1]).unwrap();
-
- if let Some(prev) = previous {
- let previousEncoderIndex = prev.encoderIndex;
- if previousEncoderIndex != encoderIndex {
- size += COST_PER_ECI;
- }
- size += prev.cachedTotalSize;
-
- Self {
- c: if c == fnc1Str {
- String::from("\u{1000}")
- } else {
- String::from(c)
- },
- encoderIndex,
- previous: Some(prev.clone()),
- cachedTotalSize: size,
- }
- } else {
- let previousEncoderIndex = 0;
- if previousEncoderIndex != encoderIndex {
- size += COST_PER_ECI;
- }
-
- Self {
- c: if c == fnc1Str {
- String::from("\u{1000}")
- } else {
- String::from(c)
- },
- encoderIndex,
- previous: None,
- cachedTotalSize: size,
- }
- }
-
- // int size = this.c == 1000 ? 1 : encoderSet.encode(c, encoderIndex).length;
- // let previousEncoderIndex = if previous.is_none() {
- // 0
- // } else {
- // previous.unwrap().encoderIndex
- // };
- // int previousEncoderIndex = previous == null ? 0 : previous.encoderIndex;
- // if previousEncoderIndex != encoderIndex {
- // size += COST_PER_ECI;
- // }
- // if prev_is_some {
- // size += previous.unwrap().cachedTotalSize;
- // }
-
- // Self {
- // c: if c == fnc1 { 1000 as char } else { c },
- // encoderIndex,
- // previous: previous,
- // cachedTotalSize: size,
- // }
- // this.c = c == fnc1 ? 1000 : c;
- // this.encoderIndex = encoderIndex;
- // this.previous = previous;
- // this.cachedTotalSize = size;
- }
-
- pub fn isFNC1(&self) -> bool {
- self.c == "\u{1000}"
- }
-}
-
-impl fmt::Display for MinimalECIInput {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut result = String::new();
- for i in 0..self.length() {
- // for (int i = 0; i < length(); i++) {
- if i > 0 {
- result.push_str(", ");
- }
- if self.isECI(i as u32).unwrap() {
- result.push_str("ECI(");
- result.push_str(&self.getECIValue(i).unwrap().to_string());
- result.push(')');
- } else if (self.charAt(i).unwrap() as u8) < 128 {
- result.push('\'');
- result.push(self.charAt(i).unwrap());
- result.push('\'');
- } else {
- result.push(self.charAt(i).unwrap());
- }
- }
- write!(f, "{}", result)
- }
-}
-
-/*
- * Copyright 2009 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 com.google.zxing.Binarizer;
-// import com.google.zxing.LuminanceSource;
-// import com.google.zxing.NotFoundException;
-
-/**
- * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
- * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
- * algorithm. However, because it picks a global black point, it cannot handle difficult shadows
- * and gradients.
- *
- * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- * @author Sean Owen
- */
-pub struct GlobalHistogramBinarizer {
- luminances: Vec,
- buckets: Vec,
- width: usize,
- height: usize,
- source: Box,
-}
-
-impl Binarizer for GlobalHistogramBinarizer {
- fn getLuminanceSource(&self) -> &Box {
- &self.source
- }
-
- // Applies simple sharpening to the row data to improve performance of the 1D Readers.
- fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result {
- let source = self.getLuminanceSource();
- let width = source.getWidth();
- let mut row = if row.getSize() < width {
- BitArray::with_size(width)
- } else {
- let mut z = row.clone();
- z.clear();
- z
- };
-
- // self.initArrays(width);
- let localLuminances = source.getRow(y, &self.luminances);
- let mut localBuckets = self.buckets.clone();
- for x in 0..width {
- // for (int x = 0; x < width; x++) {
- localBuckets
- [((localLuminances[x]) >> GlobalHistogramBinarizer::LUMINANCE_SHIFT) as usize] += 1;
- }
- let blackPoint = self.estimateBlackPoint(&localBuckets)?;
-
- if width < 3 {
- // Special case for very small images
- for x in 0..width {
- // for (int x = 0; x < width; x++) {
- if (localLuminances[x] as u32) < blackPoint {
- row.set(x);
- }
- }
- } else {
- let mut left = localLuminances[0]; // & 0xff;
- let mut center = localLuminances[1]; // & 0xff;
- for x in 1..width - 1 {
- // for (int x = 1; x < width - 1; x++) {
- let right = localLuminances[x + 1] & 0xff;
- // A simple -1 4 -1 box filter with a weight of 2.
- if ((center * 4) - left - right) as u32 / 2 < blackPoint {
- row.set(x);
- }
- left = center;
- center = right;
- }
- }
- Ok(row)
- }
-
- // Does not sharpen the data, as this call is intended to only be used by 2D Readers.
- fn getBlackMatrix(&self) -> Result {
- let source = self.getLuminanceSource();
- let width = source.getWidth();
- let height = source.getHeight();
- let mut matrix = BitMatrix::new(width as u32, height as u32)?;
-
- // Quickly calculates the histogram by sampling four rows from the image. This proved to be
- // more robust on the blackbox tests than sampling a diagonal as we used to do.
- // self.initArrays(width);
- let mut localBuckets = self.buckets.clone();
- for y in 1..5 {
- // for (int y = 1; y < 5; y++) {
- let row = height * y / 5;
- let localLuminances = source.getRow(row, &self.luminances);
- let right = (width * 4) / 5;
- let mut x = width / 5;
- while x < right {
- // for (int x = width / 5; x < right; x++) {
- let pixel = localLuminances[x];
- localBuckets[(pixel >> GlobalHistogramBinarizer::LUMINANCE_SHIFT) as usize] += 1;
- x += 1;
- }
- }
- let blackPoint = self.estimateBlackPoint(&localBuckets)?;
-
- // We delay reading the entire image luminance until the black point estimation succeeds.
- // Although we end up reading four rows twice, it is consistent with our motto of
- // "fail quickly" which is necessary for continuous scanning.
- let localLuminances = source.getMatrix();
- for y in 0..height {
- // for (int y = 0; y < height; y++) {
- let offset = y * width;
- for x in 0..width {
- // for (int x = 0; x < width; x++) {
- let pixel = localLuminances[offset + x] & 0xff;
- if (pixel as u32) < blackPoint {
- matrix.set(x as u32, y as u32);
- }
- }
- }
-
- Ok(matrix)
- }
-
- fn createBinarizer(&self, source: Box) -> Box {
- return Box::new(GlobalHistogramBinarizer::new(source));
- }
-
- fn getWidth(&self) -> usize {
- self.width
- }
-
- fn getHeight(&self) -> usize {
- self.height
- }
-}
-
-impl GlobalHistogramBinarizer {
- const LUMINANCE_BITS: usize = 5;
- const LUMINANCE_SHIFT: usize = 8 - GlobalHistogramBinarizer::LUMINANCE_BITS;
- const LUMINANCE_BUCKETS: usize = 1 << GlobalHistogramBinarizer::LUMINANCE_BITS;
- const EMPTY: [u8; 0] = [0; 0];
-
- pub fn new(source: Box) -> Self {
- Self {
- luminances: vec![0; source.getWidth()],
- buckets: vec![0; GlobalHistogramBinarizer::LUMINANCE_BUCKETS],
- width: source.getWidth(),
- height: source.getHeight(),
- source: source,
- }
- }
-
- // fn initArrays(&mut self, luminanceSize: usize) {
- // // if self.luminances.len() < luminanceSize {
- // // self.luminances = ;
- // // }
- // // // for x in 0..GlobalHistogramBinarizer::LUMINANCE_BUCKETS {
- // // // for (int x = 0; x < LUMINANCE_BUCKETS; x++) {
- // // self.buckets[x] = 0;
- // // }
- // }
-
- fn estimateBlackPoint(&self, buckets: &[u32]) -> Result {
- // Find the tallest peak in the histogram.
- let numBuckets = buckets.len();
- let mut maxBucketCount = 0;
- let mut firstPeak = 0;
- let mut firstPeakSize = 0;
- for x in 0..numBuckets {
- // for (int x = 0; x < numBuckets; x++) {
- if buckets[x] > firstPeakSize {
- firstPeak = x;
- firstPeakSize = buckets[x];
- }
- if buckets[x] > maxBucketCount {
- maxBucketCount = buckets[x];
- }
- }
-
- // Find the second-tallest peak which is somewhat far from the tallest peak.
- let mut secondPeak = 0;
- let mut secondPeakScore = 0;
- for x in 0..numBuckets {
- // for (int x = 0; x < numBuckets; x++) {
- let distanceToBiggest = x - firstPeak;
- // Encourage more distant second peaks by multiplying by square of distance.
- let score = buckets[x] * distanceToBiggest as u32 * distanceToBiggest as u32;
- if score > secondPeakScore {
- secondPeak = x;
- secondPeakScore = score;
- }
- }
-
- // Make sure firstPeak corresponds to the black peak.
- if firstPeak > secondPeak {
- let temp = firstPeak;
- firstPeak = secondPeak;
- secondPeak = temp;
- }
-
- // If there is too little contrast in the image to pick a meaningful black point, throw rather
- // than waste time trying to decode the image, and risk false positives.
- if secondPeak - firstPeak <= numBuckets / 16 {
- return Err(Exceptions::NotFoundException(
- "secondPeak - firstPeak <= numBuckets / 16 ".to_owned(),
- ));
- }
-
- // Find a valley between them that is low and closer to the white peak.
- let mut bestValley = secondPeak - 1;
- let mut bestValleyScore = -1i32;
- let mut x = secondPeak;
- while x > firstPeak {
- // for (int x = secondPeak - 1; x > firstPeak; x--) {
- let fromFirst = x - firstPeak;
- let score =
- fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]) as usize;
- if score as i32 > bestValleyScore {
- bestValley = x;
- bestValleyScore = score as i32;
- }
- x -= 1;
- }
-
- Ok((bestValley as u32) << GlobalHistogramBinarizer::LUMINANCE_SHIFT)
- }
-}
-
-/*
- * Copyright 2009 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 com.google.zxing.Binarizer;
-// import com.google.zxing.LuminanceSource;
-// import com.google.zxing.NotFoundException;
-
-/**
- * This class implements a local thresholding algorithm, which while slower than the
- * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
- * high frequency images of barcodes with black data on white backgrounds. For this application,
- * it does a much better job than a global blackpoint with severe shadows and gradients.
- * However it tends to produce artifacts on lower frequency images and is therefore not
- * a good general purpose binarizer for uses outside ZXing.
- *
- * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
- * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
- * inherently local, and only fails for horizontal gradients. We can revisit that problem later,
- * but for now it was not a win to use local blocks for 1D.
- *
- * This Binarizer is the default for the unit tests and the recommended class for library users.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-pub struct HybridBinarizer {
- //width: usize,
- //height: usize,
- //source: Box,
- ghb: GlobalHistogramBinarizer,
- // matrix :Option,
-}
-impl Binarizer for HybridBinarizer {
- fn getLuminanceSource(&self) -> &Box {
- self.ghb.getLuminanceSource()
- }
-
- fn getBlackRow(&self, y: usize, row: &mut BitArray) -> Result {
- self.ghb.getBlackRow(y, row)
- }
-
- /**
- * Calculates the final BitMatrix once for all requests. This could be called once from the
- * constructor instead, but there are some advantages to doing it lazily, such as making
- * profiling easier, and not doing heavy lifting when callers don't expect it.
- */
- fn getBlackMatrix(&self) -> Result {
- // if self.matrix.is_some() {
- // return Ok(self.matrix.clone().unwrap())
- // }
- let matrix;
- let source = self.getLuminanceSource();
- let width = source.getWidth();
- let height = source.getHeight();
- if width >= HybridBinarizer::MINIMUM_DIMENSION
- && height >= HybridBinarizer::MINIMUM_DIMENSION
- {
- let luminances = source.getMatrix();
- let mut sub_width = width >> HybridBinarizer::BLOCK_SIZE_POWER;
- if (width & HybridBinarizer::BLOCK_SIZE_MASK) != 0 {
- sub_width += 1;
- }
- let mut sub_height = height >> HybridBinarizer::BLOCK_SIZE_POWER;
- if (height & HybridBinarizer::BLOCK_SIZE_MASK) != 0 {
- sub_height += 1;
- }
- let black_points = Self::calculateBlackPoints(
- &luminances,
- sub_width as u32,
- sub_height as u32,
- width as u32,
- height as u32,
- );
-
- let mut new_matrix = BitMatrix::new(width as u32, height as u32)?;
- Self::calculateThresholdForBlock(
- &luminances,
- sub_width as u32,
- sub_height as u32,
- width as u32,
- height as u32,
- &black_points,
- &mut new_matrix,
- );
- matrix = new_matrix;
- } else {
- // If the image is too small, fall back to the global histogram approach.
- matrix = self.ghb.getBlackMatrix()?;
- }
- // dbg!(matrix.to_string());
- Ok(matrix)
- }
-
- fn createBinarizer(&self, source: Box) -> Box {
- Box::new(HybridBinarizer::new(source))
- }
-
- fn getWidth(&self) -> usize {
- self.ghb.getWidth()
- }
-
- fn getHeight(&self) -> usize {
- self.ghb.getHeight()
- }
-}
-impl HybridBinarizer {
- // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
- // So this is the smallest dimension in each axis we can accept.
- const BLOCK_SIZE_POWER: usize = 3;
- const BLOCK_SIZE: usize = 1 << HybridBinarizer::BLOCK_SIZE_POWER; // ...0100...00
- const BLOCK_SIZE_MASK: usize = HybridBinarizer::BLOCK_SIZE - 1; // ...0011...11
- const MINIMUM_DIMENSION: usize = HybridBinarizer::BLOCK_SIZE * 5;
- const MIN_DYNAMIC_RANGE: usize = 24;
-
- pub fn new(source: Box) -> Self {
- Self {
- ghb: GlobalHistogramBinarizer::new(source),
- // matrix: None,
- }
- }
-
- /**
- * For each block in the image, calculate the average black point using a 5x5 grid
- * of the blocks around it. Also handles the corner cases (fractional blocks are computed based
- * on the last pixels in the row/column which are also used in the previous block).
- */
- fn calculateThresholdForBlock(
- luminances: &[u8],
- sub_width: u32,
- sub_height: u32,
- width: u32,
- height: u32,
- black_points: &Vec>,
- matrix: &mut BitMatrix,
- ) {
- let maxYOffset = height - HybridBinarizer::BLOCK_SIZE as u32;
- let maxXOffset = width - HybridBinarizer::BLOCK_SIZE as u32;
- for y in 0..sub_height {
- // for (int y = 0; y < subHeight; y++) {
- let mut yoffset = y << HybridBinarizer::BLOCK_SIZE_POWER;
- if yoffset > maxYOffset {
- yoffset = maxYOffset;
- }
- let top = Self::cap(y, sub_height - 3);
- for x in 0..sub_width {
- // for (int x = 0; x < subWidth; x++) {
- let mut xoffset = x << HybridBinarizer::BLOCK_SIZE_POWER;
- if xoffset > maxXOffset {
- xoffset = maxXOffset;
- }
- let left = Self::cap(x, sub_width - 3);
- let mut sum = 0;
- for z in -2i32..=2 {
- // for (int z = -2; z <= 2; z++) {
- let blackRow = &black_points[(top as i32 + z) as usize];
- sum += blackRow[(left - 2) as usize]
- + blackRow[(left - 1) as usize]
- + blackRow[left as usize]
- + blackRow[(left + 1) as usize]
- + blackRow[(left + 2) as usize];
- }
- let average = sum / 25;
- Self::thresholdBlock(luminances, xoffset, yoffset, average, width, matrix);
- }
- }
- }
-
- fn cap(value: u32, max: u32) -> u32 {
- if value < 2 {
- 2
- } else {
- value.min(max)
- }
- }
-
- /**
- * Applies a single threshold to a block of pixels.
- */
- fn thresholdBlock(
- luminances: &[u8],
- xoffset: u32,
- yoffset: u32,
- threshold: u32,
- stride: u32,
- matrix: &mut BitMatrix,
- ) {
- let mut offset = yoffset * stride + xoffset;
- for y in 0..HybridBinarizer::BLOCK_SIZE {
- // for (int y = 0, offset = yoffset * stride + xoffset; y < HybridBinarizer::BLOCK_SIZE; y++, offset += stride) {
- for x in 0..HybridBinarizer::BLOCK_SIZE {
- // for (int x = 0; x < HybridBinarizer::BLOCK_SIZE; x++) {
- // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
- if luminances[offset as usize + x] as u32 <= threshold {
- matrix.set(xoffset + x as u32, yoffset + y as u32);
- }
- }
- offset += stride;
- }
- }
-
- /**
- * Calculates a single black point for each block of pixels and saves it away.
- * See the following thread for a discussion of this algorithm:
- * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
- */
- fn calculateBlackPoints(
- luminances: &[u8],
- subWidth: u32,
- subHeight: u32,
- width: u32,
- height: u32,
- ) -> Vec> {
- let maxYOffset = height as usize - HybridBinarizer::BLOCK_SIZE;
- let maxXOffset = width as usize - HybridBinarizer::BLOCK_SIZE;
- let mut blackPoints = vec![vec![0; subWidth as usize]; subHeight as usize];
- for y in 0..subHeight {
- // for (int y = 0; y < subHeight; y++) {
- let mut yoffset = y << HybridBinarizer::BLOCK_SIZE_POWER;
- if yoffset > maxYOffset as u32 {
- yoffset = maxYOffset as u32;
- }
- for x in 0..subWidth {
- // for (int x = 0; x < subWidth; x++) {
- let mut xoffset = x << HybridBinarizer::BLOCK_SIZE_POWER;
- if xoffset > maxXOffset as u32 {
- xoffset = maxXOffset as u32;
- }
- let mut sum = 0u32;
- let mut min = 0xff;
- let mut max = 0;
-
- let mut offset = yoffset * width + xoffset;
- let mut yy = 0;
- while yy < HybridBinarizer::BLOCK_SIZE {
- // for (int yy = 0, offset = yoffset * width + xoffset; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) {
- for xx in 0..HybridBinarizer::BLOCK_SIZE {
- // for (int xx = 0; xx < HybridBinarizer::BLOCK_SIZE; xx++) {
- let pixel = luminances[offset as usize + xx];
- sum += pixel as u32;
- // still looking for good contrast
- if pixel < min {
- min = pixel;
- }
- if pixel > max {
- max = pixel;
- }
- }
- // short-circuit min/max tests once dynamic range is met
- if (max - min) as usize > HybridBinarizer::MIN_DYNAMIC_RANGE {
- // finish the rest of the rows quickly
- offset += width;
- yy += 1;
- while yy < HybridBinarizer::BLOCK_SIZE {
- // for (yy++, offset += width; yy < HybridBinarizer::BLOCK_SIZE; yy++, offset += width) {
- for xx in 0..HybridBinarizer::BLOCK_SIZE {
- // for (int xx = 0; xx < BLOCK_SIZE; xx++) {
- sum += luminances[offset as usize + xx] as u32;
- }
- yy += 1;
- offset += width;
- }
- break;
- }
- yy += 1;
- offset += width;
- }
-
- // The default estimate is the average of the values in the block.
- let mut average = sum >> (HybridBinarizer::BLOCK_SIZE_POWER * 2);
- if (max - min) as usize <= HybridBinarizer::MIN_DYNAMIC_RANGE {
- // If variation within the block is low, assume this is a block with only light or only
- // dark pixels. In that case we do not want to use the average, as it would divide this
- // low contrast area into black and white pixels, essentially creating data out of noise.
- //
- // The default assumption is that the block is light/background. Since no estimate for
- // the level of dark pixels exists locally, use half the min for the block.
- average = min as u32 / 2;
-
- if y > 0 && x > 0 {
- // Correct the "white background" assumption for blocks that have neighbors by comparing
- // the pixels in this block to the previously calculated black points. This is based on
- // the fact that dark barcode symbology is always surrounded by some amount of light
- // background for which reasonable black point estimates were made. The bp estimated at
- // the boundaries is used for the interior.
-
- // The (min < bp) is arbitrary but works better than other heuristics that were tried.
- let average_neighbor_black_point: u32 = (blackPoints[y as usize - 1]
- [x as usize]
- + (2 * blackPoints[y as usize][x as usize - 1])
- + blackPoints[y as usize - 1][x as usize - 1])
- / 4;
- if (min as u32) < average_neighbor_black_point {
- average = average_neighbor_black_point;
- }
- }
- }
- blackPoints[y as usize][x as usize] = average;
- }
- }
- blackPoints
- }
-}
+mod hybrid_binarizer;
+pub use hybrid_binarizer::*;
\ No newline at end of file
diff --git a/src/common/perspective_transform.rs b/src/common/perspective_transform.rs
new file mode 100644
index 0000000..3584d09
--- /dev/null
+++ b/src/common/perspective_transform.rs
@@ -0,0 +1,213 @@
+
+/*
+ * 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;
+
+/**
+ * This class implements a perspective transform in two dimensions. Given four source and four
+ * destination points, it will compute the transformation implied between them. The code is based
+ * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
+ *
+ * @author Sean Owen
+ */
+pub struct PerspectiveTransform {
+ a11: f32,
+ a12: f32,
+ a13: f32,
+ a21: f32,
+ a22: f32,
+ a23: f32,
+ a31: f32,
+ a32: f32,
+ a33: f32,
+}
+
+impl PerspectiveTransform {
+ fn new(
+ a11: f32,
+ a21: f32,
+ a31: f32,
+ a12: f32,
+ a22: f32,
+ a32: f32,
+ a13: f32,
+ a23: f32,
+ a33: f32,
+ ) -> Self {
+ Self {
+ a11,
+ a12,
+ a13,
+ a21,
+ a22,
+ a23,
+ a31,
+ a32,
+ a33,
+ }
+ }
+
+ pub fn quadrilateralToQuadrilateral(
+ x0: f32,
+ y0: f32,
+ x1: f32,
+ y1: f32,
+ x2: f32,
+ y2: f32,
+ x3: f32,
+ y3: f32,
+ x0p: f32,
+ y0p: f32,
+ x1p: f32,
+ y1p: f32,
+ x2p: f32,
+ y2p: f32,
+ x3p: f32,
+ y3p: f32,
+ ) -> Self {
+ let q_to_s = PerspectiveTransform::quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
+ let s_to_q =
+ PerspectiveTransform::squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
+ return s_to_q.times(&q_to_s);
+ }
+
+ pub fn transform_points_single(&self, points: &mut [f32]) {
+ let a11 = self.a11;
+ let a12 = self.a12;
+ let a13 = self.a13;
+ let a21 = self.a21;
+ let a22 = self.a22;
+ let a23 = self.a23;
+ let a31 = self.a31;
+ let a32 = self.a32;
+ let a33 = self.a33;
+ let maxI = points.len() - 1; // points.length must be even
+ let mut i = 0;
+ while i < maxI {
+ // for (int i = 0; i < maxI; i += 2) {
+ let x = points[i];
+ let y = points[i + 1];
+ let denominator = a13 * x + a23 * y + a33;
+ points[i] = (a11 * x + a21 * y + a31) / denominator;
+ points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
+ i += 2;
+ }
+ }
+
+ pub fn transform_points_double(&self, x_values: &mut [f32], y_valuess: &mut [f32]) {
+ let n = x_values.len();
+ for i in 0..n {
+ // for (int i = 0; i < n; i++) {
+ let x = x_values[i];
+ let y = y_valuess[i];
+ let denominator = self.a13 * x + self.a23 * y + self.a33;
+ x_values[i] = (self.a11 * x + self.a21 * y + self.a31) / denominator;
+ y_valuess[i] = (self.a12 * x + self.a22 * y + self.a32) / denominator;
+ }
+ }
+
+ pub fn squareToQuadrilateral(
+ x0: f32,
+ y0: f32,
+ x1: f32,
+ y1: f32,
+ x2: f32,
+ y2: f32,
+ x3: f32,
+ y3: f32,
+ ) -> Self {
+ let dx3 = x0 - x1 + x2 - x3;
+ let dy3 = y0 - y1 + y2 - y3;
+ if dx3 == 0.0f32 && dy3 == 0.0f32 {
+ // Affine
+ return PerspectiveTransform::new(
+ x1 - x0,
+ x2 - x1,
+ x0,
+ y1 - y0,
+ y2 - y1,
+ y0,
+ 0.0f32,
+ 0.0f32,
+ 1.0f32,
+ );
+ } else {
+ let dx1 = x1 - x2;
+ let dx2 = x3 - x2;
+ let dy1 = y1 - y2;
+ let dy2 = y3 - y2;
+ let denominator = dx1 * dy2 - dx2 * dy1;
+ let a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
+ let a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
+ return PerspectiveTransform::new(
+ x1 - x0 + a13 * x1,
+ x3 - x0 + a23 * x3,
+ x0,
+ y1 - y0 + a13 * y1,
+ y3 - y0 + a23 * y3,
+ y0,
+ a13,
+ a23,
+ 1.0f32,
+ );
+ }
+ }
+
+ pub fn quadrilateralToSquare(
+ x0: f32,
+ y0: f32,
+ x1: f32,
+ y1: f32,
+ x2: f32,
+ y2: f32,
+ x3: f32,
+ y3: f32,
+ ) -> Self {
+ // Here, the adjoint serves as the inverse
+ return PerspectiveTransform::squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3)
+ .buildAdjoint();
+ }
+
+ fn buildAdjoint(&self) -> Self {
+ // Adjoint is the transpose of the cofactor matrix:
+ return PerspectiveTransform::new(
+ self.a22 * self.a33 - self.a23 * self.a32,
+ self.a23 * self.a31 - self.a21 * self.a33,
+ self.a21 * self.a32 - self.a22 * self.a31,
+ self.a13 * self.a32 - self.a12 * self.a33,
+ self.a11 * self.a33 - self.a13 * self.a31,
+ self.a12 * self.a31 - self.a11 * self.a32,
+ self.a12 * self.a23 - self.a13 * self.a22,
+ self.a13 * self.a21 - self.a11 * self.a23,
+ self.a11 * self.a22 - self.a12 * self.a21,
+ );
+ }
+
+ fn times(&self, other: &Self) -> Self {
+ return PerspectiveTransform::new(
+ self.a11 * other.a11 + self.a21 * other.a12 + self.a31 * other.a13,
+ self.a11 * other.a21 + self.a21 * other.a22 + self.a31 * other.a23,
+ self.a11 * other.a31 + self.a21 * other.a32 + self.a31 * other.a33,
+ self.a12 * other.a11 + self.a22 * other.a12 + self.a32 * other.a13,
+ self.a12 * other.a21 + self.a22 * other.a22 + self.a32 * other.a23,
+ self.a12 * other.a31 + self.a22 * other.a32 + self.a32 * other.a33,
+ self.a13 * other.a11 + self.a23 * other.a12 + self.a33 * other.a13,
+ self.a13 * other.a21 + self.a23 * other.a22 + self.a33 * other.a23,
+ self.a13 * other.a31 + self.a23 * other.a32 + self.a33 * other.a33,
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/common/reedsolomon/generic_gf.rs b/src/common/reedsolomon/generic_gf.rs
new file mode 100644
index 0000000..9bdfdbe
--- /dev/null
+++ b/src/common/reedsolomon/generic_gf.rs
@@ -0,0 +1,178 @@
+use std::fmt;
+
+use crate::Exceptions;
+
+use super::{GenericGFRef, GenericGFPoly};
+
+/**
+ * This class contains utility methods for performing mathematical operations over
+ * the Galois Fields. Operations use a given primitive polynomial in calculations.
+ *
+ * Throughout this package, elements of the GF are represented as an {@code int}
+ * for convenience and speed (but at the cost of memory).
+ *
+ *
+ * @author Sean Owen
+ * @author David Olivier
+ */
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GenericGF {
+ expTable: Vec,
+ logTable: Vec,
+ // zero: Box,
+ // one: Box,
+ size: usize,
+ primitive: i32,
+ generatorBase: i32,
+}
+
+impl GenericGF {
+ /**
+ * Create a representation of GF(size) using the given primitive polynomial.
+ *
+ * @param primitive irreducible polynomial whose coefficients are represented by
+ * the bits of an int, where the least-significant bit represents the constant
+ * coefficient
+ * @param size the size of the field
+ * @param b the factor b in the generator polynomial can be 0- or 1-based
+ * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
+ * In most cases it should be 1, but for QR code it is 0.
+ */
+ pub fn new(primitive: i32, size: usize, b: i32) -> Self {
+ let mut expTable = vec![0; size];
+ let mut logTable = vec![0; size];
+ let mut x = 1;
+ for i in 0..size {
+ //for (int i = 0; i < size; i++) {
+ //expTable.push(x);
+ expTable[i] = x;
+ x *= 2; // we're assuming the generator alpha is 2
+ if x >= size as i32 {
+ x ^= primitive;
+ let sz_m_1: i32 = size as i32 - 1;
+ x &= sz_m_1;
+ }
+ }
+ for i in 0..size - 1 {
+ //for (int i = 0; i < size - 1; i++) {
+ let loc: usize = expTable[i] as usize;
+ logTable[loc] = i as i32;
+ }
+ logTable[0] = 0;
+
+ // let mut p:u32;
+ // //int i;
+ // /*Initialize the table of powers of a primtive root, alpha=0x02.*/
+ // p = 1;
+ // for i in 0..size {
+ // // for (i = 0; i < 256; i++) {
+ // expTable[i] = expTable[i + size - 1] = p;
+ // p = ((p << 1) ^ (-(p as i32 >> 7) & primitive) as u32) & 0xFF;
+ // }
+ // /*Invert the table to recover the logs.*/
+ // for i in 0..size-1 {
+ // // for (i = 0; i < 255; i++)
+ // logTable[expTable[i].try_into().unwrap()] = i;
+ // /*Note that we rely on the fact that _gf->log[0]=0 below.*/
+ Self {
+ expTable,
+ logTable,
+ size,
+ primitive,
+ generatorBase: b,
+ }
+
+ // logTable[0] == 0 but this should never be used
+ // new_ggf.zero = Box::new(GenericGFPoly::new(Box::new(new_ggf), &vec![0]).unwrap());
+ // new_ggf.one = Box::new(GenericGFPoly::new(Box::new(new_ggf), &vec![1]).unwrap());
+
+ //new_ggf
+ }
+
+ // pub fn getZero(&self) -> Box {
+ // return self.zero;
+ // }
+
+ // pub fn getOne(&self) -> Box {
+ // return self.one;
+ // }
+
+ /**
+ * @return the monomial representing coefficient * x^degree
+ */
+ pub fn buildMonomial(source: GenericGFRef, degree: usize, coefficient: i32) -> GenericGFPoly {
+ if coefficient == 0 {
+ return GenericGFPoly::new(source, &vec![0]).unwrap();
+ }
+ let mut coefficients = vec![0; degree + 1];
+ coefficients[0] = coefficient;
+ return GenericGFPoly::new(source, &coefficients).unwrap();
+ }
+
+ /**
+ * Implements both addition and subtraction -- they are the same in GF(size).
+ *
+ * @return sum/difference of a and b
+ */
+ pub fn addOrSubtract(a: i32, b: i32) -> i32 {
+ return a ^ b;
+ }
+
+ /**
+ * @return 2 to the power of a in GF(size)
+ */
+ pub fn exp(&self, a: i32) -> i32 {
+ // let pos: usize = a.try_into().unwrap();
+ return self.expTable[a as usize];
+ }
+
+ /**
+ * @return base 2 log of a in GF(size)
+ */
+ pub fn log(&self, a: i32) -> Result {
+ if a == 0 {
+ return Err(Exceptions::IllegalArgumentException("".to_owned()));
+ }
+ // let pos: usize = a.try_into().unwrap();
+ return Ok(self.logTable[a as usize]);
+ }
+
+ /**
+ * @return multiplicative inverse of a
+ */
+ pub fn inverse(&self, a: i32) -> Result {
+ if a == 0 {
+ return Err(Exceptions::ArithmeticException("".to_owned()));
+ }
+ let log_t_loc: usize = a as usize;
+ let loc: usize = ((self.size as i32) - self.logTable[log_t_loc] - 1) as usize;
+ return Ok(self.expTable[loc]);
+ }
+
+ /**
+ * @return product of a and b in GF(size)
+ */
+ pub fn multiply(&self, a: i32, b: i32) -> i32 {
+ if a == 0 || b == 0 {
+ return 0;
+ }
+ let a_loc: usize = a as usize; //.try_into().unwrap();
+ let b_loc: usize = b as usize; //.try_into().unwrap();
+ let comb_loc: usize = (self.logTable[a_loc] + self.logTable[b_loc]) as usize;
+ return self.expTable[comb_loc % (self.size - 1)];
+ }
+
+ pub fn getSize(&self) -> usize {
+ return self.size;
+ }
+
+ pub fn getGeneratorBase(&self) -> i32 {
+ return self.generatorBase;
+ }
+}
+
+impl fmt::Display for GenericGF {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "GF({:#06x},{}", self.primitive, self.size)
+ }
+}
\ No newline at end of file
diff --git a/src/common/reedsolomon/generic_gf_poly.rs b/src/common/reedsolomon/generic_gf_poly.rs
new file mode 100644
index 0000000..1f9f53b
--- /dev/null
+++ b/src/common/reedsolomon/generic_gf_poly.rs
@@ -0,0 +1,340 @@
+/*
+ * 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.reedsolomon;
+
+use std::fmt;
+
+use crate::Exceptions;
+
+use super::{GenericGFRef, GenericGF};
+
+/**
+ * Represents a polynomial whose coefficients are elements of a GF.
+ * Instances of this class are immutable.
+ *
+ * Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ */
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GenericGFPoly {
+ field: GenericGFRef,
+ coefficients: Vec,
+}
+
+impl GenericGFPoly {
+ /**
+ * @param field the {@link GenericGF} instance representing the field to use
+ * to perform computations
+ * @param coefficients coefficients as ints representing elements of GF(size), arranged
+ * from most significant (highest-power term) coefficient to least significant
+ * @throws IllegalArgumentException if argument is null or empty,
+ * or if leading coefficient is 0 and this is not a
+ * constant polynomial (that is, it is not the monomial "0")
+ */
+ pub fn new(field: GenericGFRef, coefficients: &Vec) -> Result {
+ if coefficients.len() == 0 {
+ return Err(Exceptions::IllegalArgumentException(
+ "coefficients.len()".to_owned(),
+ ));
+ }
+ Ok(Self {
+ field: field,
+ coefficients: {
+ let coefficients_length = coefficients.len();
+ if coefficients_length > 1 && coefficients[0] == 0 {
+ // Leading term must be non-zero for anything except the constant polynomial "0"
+ let mut first_non_zero = 1;
+ while first_non_zero < coefficients_length && coefficients[first_non_zero] == 0
+ {
+ first_non_zero += 1;
+ }
+ if first_non_zero == coefficients_length {
+ vec![0]
+ } else {
+ let mut new_coefficients = vec![0; coefficients_length - first_non_zero];
+ let l = new_coefficients.len() - 1;
+ new_coefficients[0..=l].clone_from_slice(&coefficients[first_non_zero..]);
+ // System.arraycopy(coefficients,
+ // firstNonZero,
+ // this.coefficients,
+ // 0,
+ // this.coefficients.length);
+ new_coefficients
+ }
+ } else {
+ coefficients.to_vec()
+ }
+ },
+ })
+ }
+
+ pub fn getCoefficients(&self) -> &Vec {
+ return &self.coefficients;
+ }
+
+ /**
+ * @return degree of this polynomial
+ */
+ pub fn getDegree(&self) -> usize {
+ return self.coefficients.len() - 1;
+ }
+
+ /**
+ * @return true iff this polynomial is the monomial "0"
+ */
+ pub fn isZero(&self) -> bool {
+ return self.coefficients[0] == 0;
+ }
+
+ /**
+ * @return coefficient of x^degree term in this polynomial
+ */
+ pub fn getCoefficient(&self, degree: usize) -> i32 {
+ return self.coefficients[self.coefficients.len() - 1 - degree];
+ }
+
+ /**
+ * @return evaluation of this polynomial at a given point
+ */
+ pub fn evaluateAt(&self, a: usize) -> i32 {
+ if a == 0 {
+ // Just return the x^0 coefficient
+ return self.getCoefficient(0);
+ }
+ if a == 1 {
+ // Just the sum of the coefficients
+ let mut result = 0;
+ for coefficient in &self.coefficients {
+ //for (int coefficient : coefficients) {
+ result = GenericGF::addOrSubtract(result, *coefficient);
+ }
+ return result;
+ }
+ let mut result = self.coefficients[0];
+ let size = self.coefficients.len();
+ for i in 1..size {
+ //for (int i = 1; i < size; i++) {
+ result = GenericGF::addOrSubtract(
+ self.field.multiply(a as i32, result as i32),
+ self.coefficients[i],
+ );
+ }
+ return result;
+ }
+
+ pub fn addOrSubtract(&self, other: &GenericGFPoly) -> Result {
+ if self.field != other.field {
+ return Err(Exceptions::IllegalArgumentException(
+ "GenericGFPolys do not have same GenericGF field".to_owned(),
+ ));
+ }
+ if self.isZero() {
+ return Ok(other.clone());
+ }
+ if other.isZero() {
+ return Ok(self.clone());
+ }
+
+ let mut smallerCoefficients = self.coefficients.clone();
+ let mut largerCoefficients = other.coefficients.clone();
+ if smallerCoefficients.len() > largerCoefficients.len() {
+ let temp = smallerCoefficients;
+ smallerCoefficients = largerCoefficients;
+ largerCoefficients = temp;
+ }
+
+ let mut sumDiff = vec![0; largerCoefficients.len()];
+ let lengthDiff = largerCoefficients.len() - smallerCoefficients.len();
+ // Copy high-order terms only found in higher-degree polynomial's coefficients
+ sumDiff[0..lengthDiff].clone_from_slice(&largerCoefficients[0..lengthDiff]);
+ //System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
+
+ for i in lengthDiff..largerCoefficients.len() {
+ //for (int i = lengthDiff; i < largerCoefficients.length; i++) {
+ sumDiff[i] = GenericGF::addOrSubtract(
+ smallerCoefficients[i - lengthDiff],
+ largerCoefficients[i],
+ );
+ }
+
+ return Ok(GenericGFPoly::new(self.field, &sumDiff)?);
+ }
+
+ pub fn multiply(&self, other: &GenericGFPoly) -> Result {
+ if self.field != other.field {
+ //if (!field.equals(other.field)) {
+ return Err(Exceptions::IllegalArgumentException(
+ "GenericGFPolys do not have same GenericGF field".to_owned(),
+ ));
+ }
+ if self.isZero() || other.isZero() {
+ return Ok(self.getZero());
+ }
+ let aCoefficients = self.coefficients.clone();
+ let aLength = aCoefficients.len();
+ let bCoefficients = other.coefficients.clone();
+ let bLength = bCoefficients.len();
+ let mut product = vec![0; aLength + bLength - 1];
+ for i in 0..aLength {
+ //for (int i = 0; i < aLength; i++) {
+ let aCoeff = aCoefficients[i];
+ for j in 0..bLength {
+ //for (int j = 0; j < bLength; j++) {
+ product[i + j] = GenericGF::addOrSubtract(
+ product[i + j],
+ self.field.multiply(aCoeff, bCoefficients[j]),
+ );
+ }
+ }
+ return Ok(GenericGFPoly::new(self.field, &product)?);
+ }
+
+ pub fn multiply_with_scalar(&self, scalar: i32) -> GenericGFPoly {
+ if scalar == 0 {
+ return self.getZero();
+ }
+ if scalar == 1 {
+ return self.clone();
+ }
+ let size = self.coefficients.len();
+
+ let mut product = vec![0; size];
+ for i in 0..size {
+ //for (int i = 0; i < size; i++) {
+ product[i] = self.field.multiply(self.coefficients[i], scalar);
+ }
+ return GenericGFPoly::new(self.field, &product).unwrap();
+ }
+
+ pub fn getZero(&self) -> Self {
+ GenericGFPoly::new(self.field, &vec![0]).unwrap()
+ }
+
+ pub fn getOne(&self) -> Self {
+ GenericGFPoly::new(self.field, &vec![1]).unwrap()
+ }
+
+ pub fn multiply_by_monomial(
+ &self,
+ degree: usize,
+ coefficient: i32,
+ ) -> Result {
+ if coefficient == 0 {
+ return Ok(self.getZero());
+ }
+ let size = self.coefficients.len();
+ let mut product = vec![0; size + degree];
+ for i in 0..size {
+ //for (int i = 0; i < size; i++) {
+ product[i] = self.field.multiply(self.coefficients[i], coefficient);
+ }
+ return Ok(GenericGFPoly::new(self.field, &product)?);
+ }
+
+ pub fn divide(
+ &self,
+ other: &GenericGFPoly,
+ ) -> Result<(GenericGFPoly, GenericGFPoly), Exceptions> {
+ if self.field != other.field {
+ return Err(Exceptions::IllegalArgumentException(
+ "GenericGFPolys do not have same GenericGF field".to_owned(),
+ ));
+ }
+ if other.isZero() {
+ return Err(Exceptions::IllegalArgumentException(
+ "Divide by 0".to_owned(),
+ ));
+ }
+
+ let mut quotient = self.getZero();
+ let mut remainder = self.clone();
+
+ let denominator_leading_term = other.getCoefficient(other.getDegree());
+ let inverse_denominator_leading_term = match self.field.inverse(denominator_leading_term) {
+ Ok(val) => val,
+ Err(_issue) => {
+ return Err(Exceptions::IllegalArgumentException(
+ "arithmetic issue".to_owned(),
+ ))
+ }
+ };
+
+ while remainder.getDegree() >= other.getDegree() && !remainder.isZero() {
+ let degree_difference = remainder.getDegree() - other.getDegree();
+ let scale = self.field.multiply(
+ remainder.getCoefficient(remainder.getDegree()),
+ inverse_denominator_leading_term,
+ );
+ let term = other.multiply_by_monomial(degree_difference, scale)?;
+ let iteration_quotient = GenericGF::buildMonomial(self.field, degree_difference, scale);
+ quotient = quotient.addOrSubtract(&iteration_quotient)?;
+ remainder = remainder.addOrSubtract(&term)?;
+ }
+
+ return Ok((quotient, remainder));
+ }
+}
+
+impl fmt::Display for GenericGFPoly {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.isZero() {
+ return write!(f, "0");
+ }
+ let mut result = String::with_capacity(8 * self.getDegree());
+ for degree in (0..=self.getDegree()).rev() {
+ //for (int degree = getDegree(); degree >= 0; degree--) {
+ let mut coefficient = self.getCoefficient(degree);
+ if coefficient != 0 {
+ if coefficient < 0 {
+ if degree == self.getDegree() {
+ result.push_str("-");
+ } else {
+ result.push_str(" - ");
+ }
+ coefficient = -coefficient;
+ } else {
+ if result.len() > 0 {
+ result.push_str(" + ");
+ }
+ }
+ if degree == 0 || coefficient != 1 {
+ if let Ok(alpha_power) = self.field.log(coefficient) {
+ if alpha_power == 0 {
+ result.push_str("1");
+ } else if alpha_power == 1 {
+ result.push_str("a");
+ } else {
+ result.push_str("a^");
+ result.push_str(&format!("{}", alpha_power));
+ }
+ }
+ }
+ if degree != 0 {
+ if degree == 1 {
+ result.push_str("x");
+ } else {
+ result.push_str("x^");
+ result.push_str(&format!("{}", degree));
+ }
+ }
+ }
+ }
+ write!(f, "{}", result)
+ }
+}
\ No newline at end of file
diff --git a/src/common/reedsolomon/mod.rs b/src/common/reedsolomon/mod.rs
index 3dccd6d..0f71336 100644
--- a/src/common/reedsolomon/mod.rs
+++ b/src/common/reedsolomon/mod.rs
@@ -73,923 +73,14 @@ pub fn get_predefined_genericgf(request: PredefinedGenericGF) -> GenericGFRef {
}
}
-/**
- * This class contains utility methods for performing mathematical operations over
- * the Galois Fields. Operations use a given primitive polynomial in calculations.
- *
- * Throughout this package, elements of the GF are represented as an {@code int}
- * for convenience and speed (but at the cost of memory).
- *
- *
- * @author Sean Owen
- * @author David Olivier
- */
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct GenericGF {
- expTable: Vec,
- logTable: Vec,
- // zero: Box,
- // one: Box,
- size: usize,
- primitive: i32,
- generatorBase: i32,
-}
+mod generic_gf;
+pub use generic_gf::*;
-impl GenericGF {
- /**
- * Create a representation of GF(size) using the given primitive polynomial.
- *
- * @param primitive irreducible polynomial whose coefficients are represented by
- * the bits of an int, where the least-significant bit represents the constant
- * coefficient
- * @param size the size of the field
- * @param b the factor b in the generator polynomial can be 0- or 1-based
- * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
- * In most cases it should be 1, but for QR code it is 0.
- */
- pub fn new(primitive: i32, size: usize, b: i32) -> Self {
- let mut expTable = vec![0; size];
- let mut logTable = vec![0; size];
- let mut x = 1;
- for i in 0..size {
- //for (int i = 0; i < size; i++) {
- //expTable.push(x);
- expTable[i] = x;
- x *= 2; // we're assuming the generator alpha is 2
- if x >= size as i32 {
- x ^= primitive;
- let sz_m_1: i32 = size as i32 - 1;
- x &= sz_m_1;
- }
- }
- for i in 0..size - 1 {
- //for (int i = 0; i < size - 1; i++) {
- let loc: usize = expTable[i] as usize;
- logTable[loc] = i as i32;
- }
- logTable[0] = 0;
+mod generic_gf_poly;
+pub use generic_gf_poly::*;
- // let mut p:u32;
- // //int i;
- // /*Initialize the table of powers of a primtive root, alpha=0x02.*/
- // p = 1;
- // for i in 0..size {
- // // for (i = 0; i < 256; i++) {
- // expTable[i] = expTable[i + size - 1] = p;
- // p = ((p << 1) ^ (-(p as i32 >> 7) & primitive) as u32) & 0xFF;
- // }
- // /*Invert the table to recover the logs.*/
- // for i in 0..size-1 {
- // // for (i = 0; i < 255; i++)
- // logTable[expTable[i].try_into().unwrap()] = i;
- // /*Note that we rely on the fact that _gf->log[0]=0 below.*/
- Self {
- expTable,
- logTable,
- size,
- primitive,
- generatorBase: b,
- }
+mod reedsolomon_decoder;
+pub use reedsolomon_decoder::*;
- // logTable[0] == 0 but this should never be used
- // new_ggf.zero = Box::new(GenericGFPoly::new(Box::new(new_ggf), &vec![0]).unwrap());
- // new_ggf.one = Box::new(GenericGFPoly::new(Box::new(new_ggf), &vec![1]).unwrap());
-
- //new_ggf
- }
-
- // pub fn getZero(&self) -> Box {
- // return self.zero;
- // }
-
- // pub fn getOne(&self) -> Box {
- // return self.one;
- // }
-
- /**
- * @return the monomial representing coefficient * x^degree
- */
- pub fn buildMonomial(source: GenericGFRef, degree: usize, coefficient: i32) -> GenericGFPoly {
- if coefficient == 0 {
- return GenericGFPoly::new(source, &vec![0]).unwrap();
- }
- let mut coefficients = vec![0; degree + 1];
- coefficients[0] = coefficient;
- return GenericGFPoly::new(source, &coefficients).unwrap();
- }
-
- /**
- * Implements both addition and subtraction -- they are the same in GF(size).
- *
- * @return sum/difference of a and b
- */
- pub fn addOrSubtract(a: i32, b: i32) -> i32 {
- return a ^ b;
- }
-
- /**
- * @return 2 to the power of a in GF(size)
- */
- pub fn exp(&self, a: i32) -> i32 {
- // let pos: usize = a.try_into().unwrap();
- return self.expTable[a as usize];
- }
-
- /**
- * @return base 2 log of a in GF(size)
- */
- pub fn log(&self, a: i32) -> Result {
- if a == 0 {
- return Err(Exceptions::IllegalArgumentException("".to_owned()));
- }
- // let pos: usize = a.try_into().unwrap();
- return Ok(self.logTable[a as usize]);
- }
-
- /**
- * @return multiplicative inverse of a
- */
- pub fn inverse(&self, a: i32) -> Result {
- if a == 0 {
- return Err(Exceptions::ArithmeticException("".to_owned()));
- }
- let log_t_loc: usize = a as usize;
- let loc: usize = ((self.size as i32) - self.logTable[log_t_loc] - 1) as usize;
- return Ok(self.expTable[loc]);
- }
-
- /**
- * @return product of a and b in GF(size)
- */
- pub fn multiply(&self, a: i32, b: i32) -> i32 {
- if a == 0 || b == 0 {
- return 0;
- }
- let a_loc: usize = a as usize; //.try_into().unwrap();
- let b_loc: usize = b as usize; //.try_into().unwrap();
- let comb_loc: usize = (self.logTable[a_loc] + self.logTable[b_loc]) as usize;
- return self.expTable[comb_loc % (self.size - 1)];
- }
-
- pub fn getSize(&self) -> usize {
- return self.size;
- }
-
- pub fn getGeneratorBase(&self) -> i32 {
- return self.generatorBase;
- }
-}
-
-impl fmt::Display for GenericGF {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "GF({:#06x},{}", self.primitive, self.size)
- }
-}
-
-/*
- * 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.reedsolomon;
-
-/**
- * Represents a polynomial whose coefficients are elements of a GF.
- * Instances of this class are immutable.
- *
- * Much credit is due to William Rucklidge since portions of this code are an indirect
- * port of his C++ Reed-Solomon implementation.
- *
- * @author Sean Owen
- */
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct GenericGFPoly {
- field: GenericGFRef,
- coefficients: Vec,
-}
-
-impl GenericGFPoly {
- /**
- * @param field the {@link GenericGF} instance representing the field to use
- * to perform computations
- * @param coefficients coefficients as ints representing elements of GF(size), arranged
- * from most significant (highest-power term) coefficient to least significant
- * @throws IllegalArgumentException if argument is null or empty,
- * or if leading coefficient is 0 and this is not a
- * constant polynomial (that is, it is not the monomial "0")
- */
- pub fn new(field: GenericGFRef, coefficients: &Vec) -> Result {
- if coefficients.len() == 0 {
- return Err(Exceptions::IllegalArgumentException(
- "coefficients.len()".to_owned(),
- ));
- }
- Ok(Self {
- field: field,
- coefficients: {
- let coefficients_length = coefficients.len();
- if coefficients_length > 1 && coefficients[0] == 0 {
- // Leading term must be non-zero for anything except the constant polynomial "0"
- let mut first_non_zero = 1;
- while first_non_zero < coefficients_length && coefficients[first_non_zero] == 0
- {
- first_non_zero += 1;
- }
- if first_non_zero == coefficients_length {
- vec![0]
- } else {
- let mut new_coefficients = vec![0; coefficients_length - first_non_zero];
- let l = new_coefficients.len() - 1;
- new_coefficients[0..=l].clone_from_slice(&coefficients[first_non_zero..]);
- // System.arraycopy(coefficients,
- // firstNonZero,
- // this.coefficients,
- // 0,
- // this.coefficients.length);
- new_coefficients
- }
- } else {
- coefficients.to_vec()
- }
- },
- })
- }
-
- pub fn getCoefficients(&self) -> &Vec {
- return &self.coefficients;
- }
-
- /**
- * @return degree of this polynomial
- */
- pub fn getDegree(&self) -> usize {
- return self.coefficients.len() - 1;
- }
-
- /**
- * @return true iff this polynomial is the monomial "0"
- */
- pub fn isZero(&self) -> bool {
- return self.coefficients[0] == 0;
- }
-
- /**
- * @return coefficient of x^degree term in this polynomial
- */
- pub fn getCoefficient(&self, degree: usize) -> i32 {
- return self.coefficients[self.coefficients.len() - 1 - degree];
- }
-
- /**
- * @return evaluation of this polynomial at a given point
- */
- pub fn evaluateAt(&self, a: usize) -> i32 {
- if a == 0 {
- // Just return the x^0 coefficient
- return self.getCoefficient(0);
- }
- if a == 1 {
- // Just the sum of the coefficients
- let mut result = 0;
- for coefficient in &self.coefficients {
- //for (int coefficient : coefficients) {
- result = GenericGF::addOrSubtract(result, *coefficient);
- }
- return result;
- }
- let mut result = self.coefficients[0];
- let size = self.coefficients.len();
- for i in 1..size {
- //for (int i = 1; i < size; i++) {
- result = GenericGF::addOrSubtract(
- self.field.multiply(a as i32, result as i32),
- self.coefficients[i],
- );
- }
- return result;
- }
-
- pub fn addOrSubtract(&self, other: &GenericGFPoly) -> Result {
- if self.field != other.field {
- return Err(Exceptions::IllegalArgumentException(
- "GenericGFPolys do not have same GenericGF field".to_owned(),
- ));
- }
- if self.isZero() {
- return Ok(other.clone());
- }
- if other.isZero() {
- return Ok(self.clone());
- }
-
- let mut smallerCoefficients = self.coefficients.clone();
- let mut largerCoefficients = other.coefficients.clone();
- if smallerCoefficients.len() > largerCoefficients.len() {
- let temp = smallerCoefficients;
- smallerCoefficients = largerCoefficients;
- largerCoefficients = temp;
- }
-
- let mut sumDiff = vec![0; largerCoefficients.len()];
- let lengthDiff = largerCoefficients.len() - smallerCoefficients.len();
- // Copy high-order terms only found in higher-degree polynomial's coefficients
- sumDiff[0..lengthDiff].clone_from_slice(&largerCoefficients[0..lengthDiff]);
- //System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
-
- for i in lengthDiff..largerCoefficients.len() {
- //for (int i = lengthDiff; i < largerCoefficients.length; i++) {
- sumDiff[i] = GenericGF::addOrSubtract(
- smallerCoefficients[i - lengthDiff],
- largerCoefficients[i],
- );
- }
-
- return Ok(GenericGFPoly::new(self.field, &sumDiff)?);
- }
-
- pub fn multiply(&self, other: &GenericGFPoly) -> Result {
- if self.field != other.field {
- //if (!field.equals(other.field)) {
- return Err(Exceptions::IllegalArgumentException(
- "GenericGFPolys do not have same GenericGF field".to_owned(),
- ));
- }
- if self.isZero() || other.isZero() {
- return Ok(self.getZero());
- }
- let aCoefficients = self.coefficients.clone();
- let aLength = aCoefficients.len();
- let bCoefficients = other.coefficients.clone();
- let bLength = bCoefficients.len();
- let mut product = vec![0; aLength + bLength - 1];
- for i in 0..aLength {
- //for (int i = 0; i < aLength; i++) {
- let aCoeff = aCoefficients[i];
- for j in 0..bLength {
- //for (int j = 0; j < bLength; j++) {
- product[i + j] = GenericGF::addOrSubtract(
- product[i + j],
- self.field.multiply(aCoeff, bCoefficients[j]),
- );
- }
- }
- return Ok(GenericGFPoly::new(self.field, &product)?);
- }
-
- pub fn multiply_with_scalar(&self, scalar: i32) -> GenericGFPoly {
- if scalar == 0 {
- return self.getZero();
- }
- if scalar == 1 {
- return self.clone();
- }
- let size = self.coefficients.len();
-
- let mut product = vec![0; size];
- for i in 0..size {
- //for (int i = 0; i < size; i++) {
- product[i] = self.field.multiply(self.coefficients[i], scalar);
- }
- return GenericGFPoly::new(self.field, &product).unwrap();
- }
-
- pub fn getZero(&self) -> Self {
- GenericGFPoly::new(self.field, &vec![0]).unwrap()
- }
-
- pub fn getOne(&self) -> Self {
- GenericGFPoly::new(self.field, &vec![1]).unwrap()
- }
-
- pub fn multiply_by_monomial(
- &self,
- degree: usize,
- coefficient: i32,
- ) -> Result {
- if coefficient == 0 {
- return Ok(self.getZero());
- }
- let size = self.coefficients.len();
- let mut product = vec![0; size + degree];
- for i in 0..size {
- //for (int i = 0; i < size; i++) {
- product[i] = self.field.multiply(self.coefficients[i], coefficient);
- }
- return Ok(GenericGFPoly::new(self.field, &product)?);
- }
-
- pub fn divide(
- &self,
- other: &GenericGFPoly,
- ) -> Result<(GenericGFPoly, GenericGFPoly), Exceptions> {
- if self.field != other.field {
- return Err(Exceptions::IllegalArgumentException(
- "GenericGFPolys do not have same GenericGF field".to_owned(),
- ));
- }
- if other.isZero() {
- return Err(Exceptions::IllegalArgumentException(
- "Divide by 0".to_owned(),
- ));
- }
-
- let mut quotient = self.getZero();
- let mut remainder = self.clone();
-
- let denominator_leading_term = other.getCoefficient(other.getDegree());
- let inverse_denominator_leading_term = match self.field.inverse(denominator_leading_term) {
- Ok(val) => val,
- Err(_issue) => {
- return Err(Exceptions::IllegalArgumentException(
- "arithmetic issue".to_owned(),
- ))
- }
- };
-
- while remainder.getDegree() >= other.getDegree() && !remainder.isZero() {
- let degree_difference = remainder.getDegree() - other.getDegree();
- let scale = self.field.multiply(
- remainder.getCoefficient(remainder.getDegree()),
- inverse_denominator_leading_term,
- );
- let term = other.multiply_by_monomial(degree_difference, scale)?;
- let iteration_quotient = GenericGF::buildMonomial(self.field, degree_difference, scale);
- quotient = quotient.addOrSubtract(&iteration_quotient)?;
- remainder = remainder.addOrSubtract(&term)?;
- }
-
- return Ok((quotient, remainder));
- }
-}
-
-impl fmt::Display for GenericGFPoly {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.isZero() {
- return write!(f, "0");
- }
- let mut result = String::with_capacity(8 * self.getDegree());
- for degree in (0..=self.getDegree()).rev() {
- //for (int degree = getDegree(); degree >= 0; degree--) {
- let mut coefficient = self.getCoefficient(degree);
- if coefficient != 0 {
- if coefficient < 0 {
- if degree == self.getDegree() {
- result.push_str("-");
- } else {
- result.push_str(" - ");
- }
- coefficient = -coefficient;
- } else {
- if result.len() > 0 {
- result.push_str(" + ");
- }
- }
- if degree == 0 || coefficient != 1 {
- if let Ok(alpha_power) = self.field.log(coefficient) {
- if alpha_power == 0 {
- result.push_str("1");
- } else if alpha_power == 1 {
- result.push_str("a");
- } else {
- result.push_str("a^");
- result.push_str(&format!("{}", alpha_power));
- }
- }
- }
- if degree != 0 {
- if degree == 1 {
- result.push_str("x");
- } else {
- result.push_str("x^");
- result.push_str(&format!("{}", degree));
- }
- }
- }
- }
- write!(f, "{}", result)
- }
-}
-/*
- * 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.reedsolomon;
-
-/**
- * Implements Reed-Solomon decoding, as the name implies.
- *
- * The algorithm will not be explained here, but the following references were helpful
- * in creating this implementation:
- *
- *
- *
- * Much credit is due to William Rucklidge since portions of this code are an indirect
- * port of his C++ Reed-Solomon implementation.
- *
- * @author Sean Owen
- * @author William Rucklidge
- * @author sanfordsquires
- */
-pub struct ReedSolomonDecoder {
- field: GenericGFRef,
-}
-
-impl ReedSolomonDecoder {
- pub fn new(field: GenericGFRef) -> Self {
- Self { field: field }
- }
-
- /**
- * Decodes given set of received codewords, which include both data and error-correction
- * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
- * in the input.
- *
- * @param received data and error-correction codewords
- * @param twoS number of error-correction codewords available
- * @throws ReedSolomonException if decoding fails for any reason
- */
- pub fn decode(&self, received: &mut Vec, twoS: i32) -> Result<(), Exceptions> {
- let poly = GenericGFPoly::new(self.field, received).unwrap();
- let mut syndromeCoefficients = vec![0; twoS as usize];
- let mut noError = true;
- for i in 0..twoS {
- //for (int i = 0; i < twoS; i++) {
- let eval = poly.evaluateAt(self.field.exp(i + self.field.getGeneratorBase()) as usize);
- let len = syndromeCoefficients.len();
- syndromeCoefficients[len - 1 - i as usize] = eval;
- if eval != 0 {
- noError = false;
- }
- }
- if noError {
- return Ok(());
- }
- let syndrome = match GenericGFPoly::new(self.field, &syndromeCoefficients) {
- Ok(res) => res,
- Err(_fail) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- };
- let sigmaOmega = self.runEuclideanAlgorithm(
- &GenericGF::buildMonomial(self.field, twoS as usize, 1),
- &syndrome,
- twoS as usize,
- )?;
- let sigma = &sigmaOmega[0];
- let omega = &sigmaOmega[1];
- let errorLocations = self.findErrorLocations(&sigma)?;
- let errorMagnitudes = self.findErrorMagnitudes(&omega, &errorLocations);
- for i in 0..errorLocations.len() {
- //for (int i = 0; i < errorLocations.length; i++) {
- let log_value = self.field.log(errorLocations[i] as i32)?;
- if log_value > received.len() as i32 - 1 {
- return Ok(());
- }
- let position: isize = received.len() as isize - 1 - log_value as isize;
- if position < 0 {
- return Err(Exceptions::ReedSolomonException(
- "Bad error location".to_owned(),
- ));
- }
- received[position as usize] =
- GenericGF::addOrSubtract(received[position as usize], errorMagnitudes[i]);
- }
- Ok(())
- }
-
- fn runEuclideanAlgorithm(
- &self,
- a: &GenericGFPoly,
- b: &GenericGFPoly,
- R: usize,
- ) -> Result, Exceptions> {
- // Assume a's degree is >= b's
- let mut a = a.clone();
- let mut b = b.clone();
- if a.getDegree() < b.getDegree() {
- let temp = a;
- a = b;
- b = temp;
- }
-
- let mut rLast = a;
- let mut r = b;
- // let tLast = self.field.getZero();
- // let t = self.field.getOne();
- let mut tLast = rLast.getZero();
- let mut t = rLast.getOne();
-
- // Run Euclidean algorithm until r's degree is less than R/2
- while 2 * r.getDegree() >= R {
- let rLastLast = rLast;
- let tLastLast = tLast;
- rLast = r;
- tLast = t;
-
- // Divide rLastLast by rLast, with quotient in q and remainder in r
- if rLast.isZero() {
- // Oops, Euclidean algorithm already terminated?
- return Err(Exceptions::ReedSolomonException(
- "r_{i-1} was zero".to_owned(),
- ));
- }
- r = rLastLast;
- let mut q = r.getZero();
- let denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
- let dltInverse = match self.field.inverse(denominatorLeadingTerm) {
- Ok(inv) => inv,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "ArithmetricException".to_owned(),
- ))
- }
- };
- while r.getDegree() >= rLast.getDegree() && !r.isZero() {
- let degreeDiff = r.getDegree() - rLast.getDegree();
- let scale = self
- .field
- .multiply(r.getCoefficient(r.getDegree()), dltInverse);
- q = match q.addOrSubtract(&GenericGF::buildMonomial(self.field, degreeDiff, scale))
- {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- };
- r = match r.addOrSubtract(&match rLast.multiply_by_monomial(degreeDiff, scale) {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- }) {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- };
- }
-
- t = match (match q.multiply(&tLast) {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- })
- .addOrSubtract(&tLastLast)
- {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "IllegalArgumentException".to_owned(),
- ))
- }
- };
-
- if r.getDegree() >= rLast.getDegree() {
- return Err(Exceptions::ReedSolomonException(format!(
- "Division algorithm failed to reduce polynomial? r: {}, rLast: {}",
- r, rLast
- )));
- }
- }
-
- let sigmaTildeAtZero = t.getCoefficient(0);
- if sigmaTildeAtZero == 0 {
- return Err(Exceptions::ReedSolomonException(
- "sigmaTilde(0) was zero".to_owned(),
- ));
- }
-
- let inverse = match self.field.inverse(sigmaTildeAtZero) {
- Ok(res) => res,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "ArithmetricException".to_owned(),
- ))
- }
- };
- let sigma = t.multiply_with_scalar(inverse);
- let omega = r.multiply_with_scalar(inverse);
- return Ok(vec![sigma, omega]);
- }
-
- fn findErrorLocations(&self, errorLocator: &GenericGFPoly) -> Result, Exceptions> {
- // This is a direct application of Chien's search
- let numErrors = errorLocator.getDegree();
- if numErrors == 1 {
- // shortcut
- return Ok(vec![errorLocator.getCoefficient(1) as usize]);
- }
-
- let mut result: Vec = vec![0; numErrors];
- let mut e = 0;
- for i in 1..self.field.getSize() {
- //for (int i = 1; i < field.getSize() && e < numErrors; i++) {
- if e >= numErrors {
- break;
- }
- if errorLocator.evaluateAt(i) == 0 {
- result[e] = match self.field.inverse(i as i32) {
- Ok(res) => res as usize,
- Err(_err) => {
- return Err(Exceptions::ReedSolomonException(
- "ArithmetricException".to_owned(),
- ))
- }
- };
- e += 1;
- }
- }
- if e != numErrors {
- return Err(Exceptions::ReedSolomonException(
- "Error locator degree does not match number of roots".to_owned(),
- ));
- }
- return Ok(result);
- }
-
- fn findErrorMagnitudes(
- &self,
- errorEvaluator: &GenericGFPoly,
- errorLocations: &Vec,
- ) -> Vec {
- // This is directly applying Forney's Formula
- let s = errorLocations.len();
- let mut result = vec![0; s];
- for i in 0..s {
- //for (int i = 0; i < s; i++) {
- let xiInverse = self.field.inverse(errorLocations[i] as i32).unwrap();
- let mut denominator = 1;
- for j in 0..s {
- //for (int j = 0; j < s; j++) {
- if i != j {
- //denominator = field.multiply(denominator,
- // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
- // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
- // Below is a funny-looking workaround from Steven Parkes
- let term = self.field.multiply(errorLocations[j] as i32, xiInverse);
- let termPlus1 = if (term & 0x1) == 0 {
- term | 1
- } else {
- term & !1
- };
- denominator = self.field.multiply(denominator, termPlus1);
- }
- }
- result[i] = self.field.multiply(
- errorEvaluator.evaluateAt(xiInverse as usize),
- self.field.inverse(denominator).unwrap(),
- );
- if self.field.getGeneratorBase() != 0 {
- result[i] = self.field.multiply(result[i], xiInverse);
- }
- }
- return result;
- }
-}
-
-/*
- * 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.reedsolomon;
-
-//import java.util.ArrayList;
-//import java.util.List;
-
-/**
- *