diff --git a/src/ChecksumException.java b/src/ChecksumException.java deleted file mode 100644 index c5acbe3..0000000 --- a/src/ChecksumException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was successfully detected and decoded, but - * was not returned because its checksum feature failed. - * - * @author Sean Owen - */ -public final class ChecksumException extends ReaderException { - - private static final ChecksumException INSTANCE = new ChecksumException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private ChecksumException() { - // do nothing - } - - private ChecksumException(Throwable cause) { - super(cause); - } - - public static ChecksumException getChecksumInstance() { - return isStackTrace ? new ChecksumException() : INSTANCE; - } - - public static ChecksumException getChecksumInstance(Throwable cause) { - return isStackTrace ? new ChecksumException(cause) : INSTANCE; - } -} \ No newline at end of file diff --git a/src/FormatException.java b/src/FormatException.java deleted file mode 100644 index ebd8008..0000000 --- a/src/FormatException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was successfully detected, but some aspect of - * the content did not conform to the barcode's format rules. This could have - * been due to a mis-detection. - * - * @author Sean Owen - */ -public final class FormatException extends ReaderException { - - private static final FormatException INSTANCE = new FormatException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private FormatException() { - } - - private FormatException(Throwable cause) { - super(cause); - } - - public static FormatException getFormatInstance() { - return isStackTrace ? new FormatException() : INSTANCE; - } - - public static FormatException getFormatInstance(Throwable cause) { - return isStackTrace ? new FormatException(cause) : INSTANCE; - } -} diff --git a/src/NotFoundException.java b/src/NotFoundException.java deleted file mode 100644 index a00c3fe..0000000 --- a/src/NotFoundException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was not found in the image. It might have been - * partially detected but could not be confirmed. - * - * @author Sean Owen - */ -public final class NotFoundException extends ReaderException { - - private static final NotFoundException INSTANCE = new NotFoundException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private NotFoundException() { - // do nothing - } - - public static NotFoundException getNotFoundInstance() { - return isStackTrace ? new NotFoundException() : INSTANCE; - } - -} \ No newline at end of file diff --git a/src/ReaderException.java b/src/ReaderException.java deleted file mode 100644 index c0690e3..0000000 --- a/src/ReaderException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * The general exception class throw when something goes wrong during decoding of a barcode. - * This includes, but is not limited to, failing checksums / error correction algorithms, being - * unable to locate finder timing patterns, and so on. - * - * @author Sean Owen - */ -public abstract class ReaderException extends Exception { - - // disable stack traces when not running inside test units - protected static boolean isStackTrace = - System.getProperty("surefire.test.class.path") != null; - protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0]; - - ReaderException() { - // do nothing - } - - ReaderException(Throwable cause) { - super(cause); - } - - // Prevent stack traces from being taken - @Override - public final synchronized Throwable fillInStackTrace() { - return null; - } - - /** - * For testing only. Controls whether library exception classes include stack traces or not. - * Defaults to false, unless running in the project's unit testing harness. - * - * @param enabled if true, enables stack traces in library exception classes - * @since 3.5.0 - */ - public static void setStackTrace(boolean enabled) { - isStackTrace = enabled; - } - -} diff --git a/src/WriterException.java b/src/WriterException.java deleted file mode 100644 index d2a37bc..0000000 --- a/src/WriterException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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; - -/** - * A base class which covers the range of exceptions which may occur when encoding a barcode using - * the Writer framework. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class WriterException extends Exception { - - public WriterException() { - } - - public WriterException(String message) { - super(message); - } - - public WriterException(Throwable cause) { - super(cause); - } - -} diff --git a/src/common/detector/MonochromeRectangleDetector.rs b/src/common/detector/MonochromeRectangleDetector.rs index 9a59d91..e69de29 100644 --- a/src/common/detector/MonochromeRectangleDetector.rs +++ b/src/common/detector/MonochromeRectangleDetector.rs @@ -1,303 +0,0 @@ -/* - * 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::common::BitMatrix; -use crate::{NotFoundException, RXingResultPoint}; - -/** - *

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, - } - } - - /** - *

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, NotFoundException> { - let height = self.image.getHeight(); - let width = self.image.getWidth(); - let halfHeight = height / 2; - let halfWidth = width / 2; - let deltaY = 1.max( height / (MAX_MODULES * 8)); - let deltaX = 1.max( width / (MAX_MODULES * 8)); - - let top = 0; - let bottom = height; - let left = 0; - let right = width; - let pointA = self.findCornerFromCenter( - halfWidth, - 0, - left, - right, - halfHeight, - -deltaY, - top, - bottom, - halfWidth / 2, - )?; - top = pointA.getY() - 1; - let pointB = self.findCornerFromCenter( - halfWidth, - -deltaX, - left, - right, - halfHeight, - 0, - top, - bottom, - halfHeight / 2, - )?; - left = pointB.getX() - 1; - let pointC = self.findCornerFromCenter( - halfWidth, - deltaX, - left, - right, - halfHeight, - 0, - top, - bottom, - halfHeight / 2, - )?; - right = pointC.getX() + 1; - let pointD = self.findCornerFromCenter( - halfWidth, - 0, - left, - right, - halfHeight, - deltaY, - top, - bottom, - halfWidth / 2, - )?; - bottom = pointD.getY() + 1; - - // 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 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 lastRange : Option> = None; - let y: i32 = centerY; - let 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 (lastRange .is_none()) { - return Err(NotFoundException.getNotFoundInstance()); - } - // 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 RXingResultPoint::new( - lastRange[if deltaY > 0 { 0 } else { 1 }], - lastY, - ); - } - return RXingResultPoint::new(lastRange[0], lastY); - } else { - return RXingResultPoint::new(lastRange[1], lastY); - } - } else { - let lastX = x - deltaX; - if (lastRange[0] < centerY) { - if (lastRange[1] > centerY) { - return RXingResultPoint::new( - lastX, - lastRange[if deltaX < 0 { 0 } else { 1 }], - ); - } - return RXingResultPoint::new(lastX, lastRange[0]); - } else { - return RXingResultPoint::new(lastX, lastRange[1]); - } - } - } - lastRange = range; - y += deltaY; - x += deltaX - } - return Err(NotFoundException.getNotFoundInstance()); - } - - /** - * 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 start = center; - while (start >= minDim) { - if (if horizontal { - self.image.get(start, fixedDimension) - } else { - self.image.get(fixedDimension, start) - }) { - start = start - 1; - } else { - let whiteRunStart = start; - start = start - 1; - while start >= minDim - && !(if horizontal { - self.image.get(start, fixedDimension) - } else { - self.image.get(fixedDimension, start) - }) - { - start = start - 1; - } - let whiteRunSize = whiteRunStart - start; - if (start < minDim || whiteRunSize > maxWhiteRun) { - start = whiteRunStart; - break; - } - } - } - start = start + 1; - - // Then try right/down - let end = center; - while (end < maxDim) { - if (if horizontal { - self.image.get(end, fixedDimension) - } else { - self.image.get(fixedDimension, end) - }) { - end = end + 1; - } else { - let whiteRunStart = end; - end = end + 1; - while end < maxDim - && !(if horizontal { - self.image.get(end, fixedDimension) - } else { - self.image.get(fixedDimension, end) - }) - { - 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 - }; - } -} diff --git a/src/common/detector/WhiteRectangleDetector.rs b/src/common/detector/WhiteRectangleDetector.rs index 770d45c..e69de29 100644 --- a/src/common/detector/WhiteRectangleDetector.rs +++ b/src/common/detector/WhiteRectangleDetector.rs @@ -1,345 +0,0 @@ -/* - * 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::{NotFoundException,RXingResultPoint}; -use crate::common::BitMatrix; - -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() / 2, image.getHeight() / 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 new_wrd : Self; - new_wrd.image = image; - new_wrd.height = image.getHeight(); - new_wrd.width = image.getWidth(); - let halfsize = initSize / 2; - new_wrd.leftInit = x - halfsize; - new_wrd.rightInit = x + halfsize; - new_wrd.upInit = y - halfsize; - new_wrd.downInit = y + halfsize; - if (new_wrd.upInit < 0 || new_wrd.leftInit < 0 || new_wrd.downInit >= new_wrd.height || new_wrd.rightInit >= new_wrd.width) { - return Err( NotFoundException.getNotFoundInstance()); - } - - Ok(new_wrd) - } - - /** - *

- * 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, NotFoundException> { - - let left :i32= self.leftInit; - let right:i32 = self.rightInit; - let up:i32 = self.upInit; - let down:i32 = self.downInit; - let sizeExceeded = false; - let aBlackPointFoundOnBorder = true; - - let atLeastOneBlackPointFoundOnRight = false; - let atLeastOneBlackPointFoundOnBottom = false; - let atLeastOneBlackPointFoundOnLeft = false; - let atLeastOneBlackPointFoundOnTop = false; - - while (aBlackPointFoundOnBorder) { - - aBlackPointFoundOnBorder = false; - - // ..... - // . | - // ..... - let rightBorderNotWhite = true; - while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < self.width) { - rightBorderNotWhite = self.containsBlackPoint(up, down, right, false); - if (rightBorderNotWhite) { - right += 1; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnRight = true; - } else if (!atLeastOneBlackPointFoundOnRight) { - right+=1; - } - } - - if (right >= self.width) { - sizeExceeded = true; - break; - } - - // ..... - // . . - // .___. - let bottomBorderNotWhite = true; - while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < self.height) { - bottomBorderNotWhite = self.containsBlackPoint(left, right, down, true); - if (bottomBorderNotWhite) { - down+=1; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnBottom = true; - } else if (!atLeastOneBlackPointFoundOnBottom) { - down+=1; - } - } - - if (down >= self.height) { - sizeExceeded = true; - break; - } - - // ..... - // | . - // ..... - let leftBorderNotWhite = true; - while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) { - leftBorderNotWhite = self.containsBlackPoint(up, down, left, false); - if (leftBorderNotWhite) { - left-=1; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnLeft = true; - } else if (!atLeastOneBlackPointFoundOnLeft) { - left-=1; - } - } - - if (left < 0) { - sizeExceeded = true; - break; - } - - // .___. - // . . - // ..... - let topBorderNotWhite = true; - while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) { - topBorderNotWhite = self.containsBlackPoint(left, right, up, true); - if (topBorderNotWhite) { - up-=1; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnTop = true; - } else if (!atLeastOneBlackPointFoundOnTop) { - up-=1; - } - } - - if (up < 0) { - sizeExceeded = true; - break; - } - - } - - if (!sizeExceeded) { - - let maxSize = right - left; - - let mut z: Option = None; - let mut i = 1; - while z.is_none() && i < maxSize { - //for (int i = 1; z == null && i < maxSize; i++) { - z = self.getBlackPointOnSegment(left, down - i, left + i, down); - i+=1; - } - - if (z .is_none()) { - return Err( NotFoundException.getNotFoundInstance()); - } - - let mut t : Option = None; - //go down right - let mut i = 1; - while t.is_none() && i < maxSize { - //for (int i = 1; t == null && i < maxSize; i++) { - t = self.getBlackPointOnSegment(left, up + i, left + i, up); - i+=1; - } - - if (t .is_none()) { - return Err( NotFoundException.getNotFoundInstance()); - } - - let mut x : Option = None; - //go down left - let mut i = 1; - while x.is_none() && i < maxSize { - //for (int i = 1; x == null && i < maxSize; i++) { - x = self.getBlackPointOnSegment(right, up + i, right - i, up); - i += 1; - } - - if (x .is_none()) { - return Err( NotFoundException.getNotFoundInstance()); - } - - let mut y : Option = None; - //go up left - let mut i = 1; - while y.is_none() && i < maxSize { - //for (int i = 1; y == null && i < maxSize; i++) { - y = self.getBlackPointOnSegment(right, down - i, right - i, down); - i+=1; - } - - if (y .is_none()) { - return Err( NotFoundException.getNotFoundInstance()); - } - - return self.centerEdges(y, z, x, t); - - } else { - return Err( NotFoundException.getNotFoundInstance()); - } - } - - fn getBlackPointOnSegment(&self, aX:f32, aY:f32, bX:f32, bY:f32) -> Option { - let dist = MathUtils::round(MathUtils::distance_float(aX, aY, bX, bY)); - let xStep :f32= (bX - aX) / dist; - let yStep:f32 = (bY - aY) / dist; - - for i in 0..dist { - let x = MathUtils::round(aX + i * xStep); - let y = MathUtils::round(aY + i * yStep); - if (self.image.get(x, y)) { - return RXingResultPoint::new(x, y); - } - } - 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 centerEdges( &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.into() / 2.0f32) { - return vec! - [ RXingResultPoint::new(ti - CORR, tj + CORR), - RXingResultPoint::new(zi + CORR, zj + CORR), - RXingResultPoint::new(xi - CORR, xj - CORR), - RXingResultPoint::new(yi + CORR, yj - CORR)]; - } else { - return vec![ - RXingResultPoint::new(ti + CORR, tj + CORR), - RXingResultPoint::new(zi + CORR, zj - CORR), - RXingResultPoint::new(xi - CORR, xj + CORR), - RXingResultPoint::new(yi - CORR, yj - CORR)]; - } - } - - /** - * 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 containsBlackPoint(&self, a:i32, b:i32, fixed:i32, horizontal:bool) -> bool { - - if (horizontal) { - - for x in a..=b { - if (self.image.get(x, fixed)) { - return true; - } - } - } else { - - for y in a..=b { - if (self.image.get(fixed, y)) { - return true; - } - } - } - - return false; - } - -} diff --git a/src/common/detector/mod.rs b/src/common/detector/mod.rs index 6f1e495..fb6c2f7 100644 --- a/src/common/detector/mod.rs +++ b/src/common/detector/mod.rs @@ -1,7 +1,666 @@ -mod MonochromeRectangleDetector; -mod WhiteRectangleDetector; - pub mod MathUtils; -pub use MonochromeRectangleDetector::*; -pub use WhiteRectangleDetector::*; \ No newline at end of file +/* + * 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::common::BitMatrix; +use crate::{NotFoundException, RXingResultPoint}; + +/** + *

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 } + } + + /** + *

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, NotFoundException> { + let height = self.image.getHeight(); + let width = self.image.getWidth(); + let halfHeight = height / 2; + let halfWidth = width / 2; + let deltaY = 1.max(height / (MAX_MODULES * 8)); + let deltaX = 1.max(width / (MAX_MODULES * 8)); + + let top = 0; + let bottom = height; + let left = 0; + let right = width; + let pointA = self.findCornerFromCenter( + halfWidth, + 0, + left, + right, + halfHeight, + -deltaY, + top, + bottom, + halfWidth / 2, + )?; + top = pointA.getY() - 1; + let pointB = self.findCornerFromCenter( + halfWidth, + -deltaX, + left, + right, + halfHeight, + 0, + top, + bottom, + halfHeight / 2, + )?; + left = pointB.getX() - 1; + let pointC = self.findCornerFromCenter( + halfWidth, + deltaX, + left, + right, + halfHeight, + 0, + top, + bottom, + halfHeight / 2, + )?; + right = pointC.getX() + 1; + let pointD = self.findCornerFromCenter( + halfWidth, + 0, + left, + right, + halfHeight, + deltaY, + top, + bottom, + halfWidth / 2, + )?; + bottom = pointD.getY() + 1; + + // 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 lastRange: Option> = None; + let y: i32 = centerY; + let 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 (lastRange.is_none()) { + return Err(NotFoundException {}); + } + // 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 RXingResultPoint::new( + lastRange?[if deltaY > 0 { 0 } else { 1 }], + lastY, + ); + } + return RXingResultPoint::new(lastRange?[0], lastY); + } else { + return RXingResultPoint::new(lastRange?[1], lastY); + } + } else { + let lastX = x - deltaX; + if (lastRange?[0] < centerY) { + if (lastRange?[1] > centerY) { + return RXingResultPoint::new( + lastX, + lastRange?[if deltaX < 0 { 0 } else { 1 }], + ); + } + return RXingResultPoint::new(lastX, lastRange?[0]); + } else { + return RXingResultPoint::new(lastX, lastRange?[1]); + } + } + } + lastRange = range; + y += deltaY; + x += deltaX + } + return Err(NotFoundException {}); + } + + /** + * 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 start = center; + while (start >= minDim) { + if (if horizontal { + self.image.get(start, fixedDimension) + } else { + self.image.get(fixedDimension, start) + }) { + start = start - 1; + } else { + let whiteRunStart = start; + start = start - 1; + while start >= minDim + && !(if horizontal { + self.image.get(start, fixedDimension) + } else { + self.image.get(fixedDimension, start) + }) + { + start = start - 1; + } + let whiteRunSize = whiteRunStart - start; + if (start < minDim || whiteRunSize > maxWhiteRun) { + start = whiteRunStart; + break; + } + } + } + start = start + 1; + + // Then try right/down + let end = center; + while (end < maxDim) { + if (if horizontal { + self.image.get(end, fixedDimension) + } else { + self.image.get(fixedDimension, end) + }) { + end = end + 1; + } else { + let whiteRunStart = end; + end = end + 1; + while end < maxDim + && !(if horizontal { + self.image.get(end, fixedDimension) + } else { + self.image.get(fixedDimension, end) + }) + { + 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; + +use crate::common::BitMatrix; +use crate::{NotFoundException, RXingResultPoint}; + +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() / 2, + image.getHeight() / 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 new_wrd: Self; + new_wrd.image = image; + new_wrd.height = image.getHeight(); + new_wrd.width = image.getWidth(); + let halfsize = initSize / 2; + new_wrd.leftInit = x - halfsize; + new_wrd.rightInit = x + halfsize; + new_wrd.upInit = y - halfsize; + new_wrd.downInit = y + halfsize; + if (new_wrd.upInit < 0 + || new_wrd.leftInit < 0 + || new_wrd.downInit >= new_wrd.height + || new_wrd.rightInit >= new_wrd.width) + { + return Err(NotFoundException {}); + } + + Ok(new_wrd) + } + + /** + *

+ * 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, NotFoundException> { + let left: i32 = self.leftInit; + let right: i32 = self.rightInit; + let up: i32 = self.upInit; + let down: i32 = self.downInit; + let sizeExceeded = false; + let aBlackPointFoundOnBorder = true; + + let atLeastOneBlackPointFoundOnRight = false; + let atLeastOneBlackPointFoundOnBottom = false; + let atLeastOneBlackPointFoundOnLeft = false; + let atLeastOneBlackPointFoundOnTop = false; + + while (aBlackPointFoundOnBorder) { + aBlackPointFoundOnBorder = false; + + // ..... + // . | + // ..... + let rightBorderNotWhite = true; + while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < self.width) + { + rightBorderNotWhite = self.containsBlackPoint(up, down, right, false); + if (rightBorderNotWhite) { + right += 1; + aBlackPointFoundOnBorder = true; + atLeastOneBlackPointFoundOnRight = true; + } else if (!atLeastOneBlackPointFoundOnRight) { + right += 1; + } + } + + if (right >= self.width) { + sizeExceeded = true; + break; + } + + // ..... + // . . + // .___. + let bottomBorderNotWhite = true; + while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) + && down < self.height) + { + bottomBorderNotWhite = self.containsBlackPoint(left, right, down, true); + if (bottomBorderNotWhite) { + down += 1; + aBlackPointFoundOnBorder = true; + atLeastOneBlackPointFoundOnBottom = true; + } else if (!atLeastOneBlackPointFoundOnBottom) { + down += 1; + } + } + + if (down >= self.height) { + sizeExceeded = true; + break; + } + + // ..... + // | . + // ..... + let leftBorderNotWhite = true; + while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) { + leftBorderNotWhite = self.containsBlackPoint(up, down, left, false); + if (leftBorderNotWhite) { + left -= 1; + aBlackPointFoundOnBorder = true; + atLeastOneBlackPointFoundOnLeft = true; + } else if (!atLeastOneBlackPointFoundOnLeft) { + left -= 1; + } + } + + if (left < 0) { + sizeExceeded = true; + break; + } + + // .___. + // . . + // ..... + let topBorderNotWhite = true; + while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) { + topBorderNotWhite = self.containsBlackPoint(left, right, up, true); + if (topBorderNotWhite) { + up -= 1; + aBlackPointFoundOnBorder = true; + atLeastOneBlackPointFoundOnTop = true; + } else if (!atLeastOneBlackPointFoundOnTop) { + up -= 1; + } + } + + if (up < 0) { + sizeExceeded = true; + break; + } + } + + if (!sizeExceeded) { + let maxSize = right - left; + + let mut z: Option = None; + let mut i = 1; + while z.is_none() && i < maxSize { + //for (int i = 1; z == null && i < maxSize; i++) { + z = self.getBlackPointOnSegment(left, down - i, left + i, down); + i += 1; + } + + if (z.is_none()) { + return Err(NotFoundException {}); + } + + let mut t: Option = None; + //go down right + let mut i = 1; + while t.is_none() && i < maxSize { + //for (int i = 1; t == null && i < maxSize; i++) { + t = self.getBlackPointOnSegment(left, up + i, left + i, up); + i += 1; + } + + if (t.is_none()) { + return Err(NotFoundException {}); + } + + let mut x: Option = None; + //go down left + let mut i = 1; + while x.is_none() && i < maxSize { + //for (int i = 1; x == null && i < maxSize; i++) { + x = self.getBlackPointOnSegment(right, up + i, right - i, up); + i += 1; + } + + if (x.is_none()) { + return Err(NotFoundException {}); + } + + let mut y: Option = None; + //go up left + let mut i = 1; + while y.is_none() && i < maxSize { + //for (int i = 1; y == null && i < maxSize; i++) { + y = self.getBlackPointOnSegment(right, down - i, right - i, down); + i += 1; + } + + if (y.is_none()) { + return Err(NotFoundException {}); + } + + return Ok(self.centerEdges(y.unwrap(), z.unwrap(), x.unwrap(), t.unwrap())); + } else { + return Err(NotFoundException {}); + } + } + + fn getBlackPointOnSegment( + &self, + aX: f32, + aY: f32, + bX: f32, + bY: f32, + ) -> Option { + let dist = MathUtils::round(MathUtils::distance_float(aX, aY, bX, bY)); + let xStep: f32 = (bX - aX) / dist.into(); + let yStep: f32 = (bY - aY) / dist.into(); + + for i in 0..dist { + let x = MathUtils::round(aX + i.into() * xStep); + let y = MathUtils::round(aY + i.into() * yStep); + if (self.image.get(x, y)) { + return RXingResultPoint::new(x, y); + } + } + 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 centerEdges( + &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.into() / 2.0f32) { + return vec![ + RXingResultPoint::new(ti - CORR, tj + CORR), + RXingResultPoint::new(zi + CORR, zj + CORR), + RXingResultPoint::new(xi - CORR, xj - CORR), + RXingResultPoint::new(yi + CORR, yj - CORR), + ]; + } else { + return vec![ + RXingResultPoint::new(ti + CORR, tj + CORR), + RXingResultPoint::new(zi + CORR, zj - CORR), + RXingResultPoint::new(xi - CORR, xj + CORR), + RXingResultPoint::new(yi - CORR, yj - CORR), + ]; + } + } + + /** + * 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 containsBlackPoint(&self, a: i32, b: i32, fixed: i32, horizontal: bool) -> bool { + if (horizontal) { + for x in a..=b { + if (self.image.get(x, fixed)) { + return true; + } + } + } else { + for y in a..=b { + if (self.image.get(fixed, y)) { + return true; + } + } + } + + return false; + } +} diff --git a/src/common/reedsolomon/GenericGF.java b/src/common/reedsolomon/GenericGF.java deleted file mode 100644 index 2b32566..0000000 --- a/src/common/reedsolomon/GenericGF.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

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 - */ -public final class GenericGF { - - public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 - public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 - public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 - public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 - public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 - public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 - public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256; - public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6; - - private final int[] expTable; - private final int[] logTable; - private final GenericGFPoly zero; - private final GenericGFPoly one; - private final int size; - private final int primitive; - private final int generatorBase; - - /** - * 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. - */ - public GenericGF(int primitive, int size, int b) { - this.primitive = primitive; - this.size = size; - this.generatorBase = b; - - expTable = new int[size]; - logTable = new int[size]; - int x = 1; - for (int i = 0; i < size; i++) { - expTable[i] = x; - x *= 2; // we're assuming the generator alpha is 2 - if (x >= size) { - x ^= primitive; - x &= size - 1; - } - } - for (int i = 0; i < size - 1; i++) { - logTable[expTable[i]] = i; - } - // logTable[0] == 0 but this should never be used - zero = new GenericGFPoly(this, new int[]{0}); - one = new GenericGFPoly(this, new int[]{1}); - } - - GenericGFPoly getZero() { - return zero; - } - - GenericGFPoly getOne() { - return one; - } - - /** - * @return the monomial representing coefficient * x^degree - */ - GenericGFPoly buildMonomial(int degree, int coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient == 0) { - return zero; - } - int[] coefficients = new int[degree + 1]; - coefficients[0] = coefficient; - return new GenericGFPoly(this, coefficients); - } - - /** - * Implements both addition and subtraction -- they are the same in GF(size). - * - * @return sum/difference of a and b - */ - static int addOrSubtract(int a, int b) { - return a ^ b; - } - - /** - * @return 2 to the power of a in GF(size) - */ - int exp(int a) { - return expTable[a]; - } - - /** - * @return base 2 log of a in GF(size) - */ - int log(int a) { - if (a == 0) { - throw new IllegalArgumentException(); - } - return logTable[a]; - } - - /** - * @return multiplicative inverse of a - */ - int inverse(int a) { - if (a == 0) { - throw new ArithmeticException(); - } - return expTable[size - logTable[a] - 1]; - } - - /** - * @return product of a and b in GF(size) - */ - int multiply(int a, int b) { - if (a == 0 || b == 0) { - return 0; - } - return expTable[(logTable[a] + logTable[b]) % (size - 1)]; - } - - public int getSize() { - return size; - } - - public int getGeneratorBase() { - return generatorBase; - } - - @Override - public String toString() { - return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')'; - } - -} diff --git a/src/common/reedsolomon/GenericGFPoly.java b/src/common/reedsolomon/GenericGFPoly.java deleted file mode 100644 index 03f6743..0000000 --- a/src/common/reedsolomon/GenericGFPoly.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.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 - */ -final class GenericGFPoly { - - private final GenericGF field; - private final int[] coefficients; - - /** - * @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") - */ - GenericGFPoly(GenericGF field, int[] coefficients) { - if (coefficients.length == 0) { - throw new IllegalArgumentException(); - } - this.field = field; - int coefficientsLength = coefficients.length; - if (coefficientsLength > 1 && coefficients[0] == 0) { - // Leading term must be non-zero for anything except the constant polynomial "0" - int firstNonZero = 1; - while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { - firstNonZero++; - } - if (firstNonZero == coefficientsLength) { - this.coefficients = new int[]{0}; - } else { - this.coefficients = new int[coefficientsLength - firstNonZero]; - System.arraycopy(coefficients, - firstNonZero, - this.coefficients, - 0, - this.coefficients.length); - } - } else { - this.coefficients = coefficients; - } - } - - int[] getCoefficients() { - return coefficients; - } - - /** - * @return degree of this polynomial - */ - int getDegree() { - return coefficients.length - 1; - } - - /** - * @return true iff this polynomial is the monomial "0" - */ - boolean isZero() { - return coefficients[0] == 0; - } - - /** - * @return coefficient of x^degree term in this polynomial - */ - int getCoefficient(int degree) { - return coefficients[coefficients.length - 1 - degree]; - } - - /** - * @return evaluation of this polynomial at a given point - */ - int evaluateAt(int a) { - if (a == 0) { - // Just return the x^0 coefficient - return getCoefficient(0); - } - if (a == 1) { - // Just the sum of the coefficients - int result = 0; - for (int coefficient : coefficients) { - result = GenericGF.addOrSubtract(result, coefficient); - } - return result; - } - int result = coefficients[0]; - int size = coefficients.length; - for (int i = 1; i < size; i++) { - result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); - } - return result; - } - - GenericGFPoly addOrSubtract(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (isZero()) { - return other; - } - if (other.isZero()) { - return this; - } - - int[] smallerCoefficients = this.coefficients; - int[] largerCoefficients = other.coefficients; - if (smallerCoefficients.length > largerCoefficients.length) { - int[] temp = smallerCoefficients; - smallerCoefficients = largerCoefficients; - largerCoefficients = temp; - } - int[] sumDiff = new int[largerCoefficients.length]; - int lengthDiff = largerCoefficients.length - smallerCoefficients.length; - // Copy high-order terms only found in higher-degree polynomial's coefficients - System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); - - for (int i = lengthDiff; i < largerCoefficients.length; i++) { - sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); - } - - return new GenericGFPoly(field, sumDiff); - } - - GenericGFPoly multiply(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (isZero() || other.isZero()) { - return field.getZero(); - } - int[] aCoefficients = this.coefficients; - int aLength = aCoefficients.length; - int[] bCoefficients = other.coefficients; - int bLength = bCoefficients.length; - int[] product = new int[aLength + bLength - 1]; - for (int i = 0; i < aLength; i++) { - int aCoeff = aCoefficients[i]; - for (int j = 0; j < bLength; j++) { - product[i + j] = GenericGF.addOrSubtract(product[i + j], - field.multiply(aCoeff, bCoefficients[j])); - } - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly multiply(int scalar) { - if (scalar == 0) { - return field.getZero(); - } - if (scalar == 1) { - return this; - } - int size = coefficients.length; - int[] product = new int[size]; - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], scalar); - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly multiplyByMonomial(int degree, int coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient == 0) { - return field.getZero(); - } - int size = coefficients.length; - int[] product = new int[size + degree]; - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], coefficient); - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly[] divide(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (other.isZero()) { - throw new IllegalArgumentException("Divide by 0"); - } - - GenericGFPoly quotient = field.getZero(); - GenericGFPoly remainder = this; - - int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); - int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); - - while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { - int degreeDifference = remainder.getDegree() - other.getDegree(); - int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); - GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale); - GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale); - quotient = quotient.addOrSubtract(iterationQuotient); - remainder = remainder.addOrSubtract(term); - } - - return new GenericGFPoly[] { quotient, remainder }; - } - - @Override - public String toString() { - if (isZero()) { - return "0"; - } - StringBuilder result = new StringBuilder(8 * getDegree()); - for (int degree = getDegree(); degree >= 0; degree--) { - int coefficient = getCoefficient(degree); - if (coefficient != 0) { - if (coefficient < 0) { - if (degree == getDegree()) { - result.append("-"); - } else { - result.append(" - "); - } - coefficient = -coefficient; - } else { - if (result.length() > 0) { - result.append(" + "); - } - } - if (degree == 0 || coefficient != 1) { - int alphaPower = field.log(coefficient); - if (alphaPower == 0) { - result.append('1'); - } else if (alphaPower == 1) { - result.append('a'); - } else { - result.append("a^"); - result.append(alphaPower); - } - } - if (degree != 0) { - if (degree == 1) { - result.append('x'); - } else { - result.append("x^"); - result.append(degree); - } - } - } - } - return result.toString(); - } - -} diff --git a/src/common/reedsolomon/ReedSolomonDecoder.java b/src/common/reedsolomon/ReedSolomonDecoder.java deleted file mode 100644 index 2593456..0000000 --- a/src/common/reedsolomon/ReedSolomonDecoder.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.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 - */ -public final class ReedSolomonDecoder { - - private final GenericGF field; - - public ReedSolomonDecoder(GenericGF field) { - this.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 - */ - public void decode(int[] received, int twoS) throws ReedSolomonException { - GenericGFPoly poly = new GenericGFPoly(field, received); - int[] syndromeCoefficients = new int[twoS]; - boolean noError = true; - for (int i = 0; i < twoS; i++) { - int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase())); - syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; - if (eval != 0) { - noError = false; - } - } - if (noError) { - return; - } - GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients); - GenericGFPoly[] sigmaOmega = - runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); - GenericGFPoly sigma = sigmaOmega[0]; - GenericGFPoly omega = sigmaOmega[1]; - int[] errorLocations = findErrorLocations(sigma); - int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations); - for (int i = 0; i < errorLocations.length; i++) { - int position = received.length - 1 - field.log(errorLocations[i]); - if (position < 0) { - throw new ReedSolomonException("Bad error location"); - } - received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]); - } - } - - private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R) - throws ReedSolomonException { - // Assume a's degree is >= b's - if (a.getDegree() < b.getDegree()) { - GenericGFPoly temp = a; - a = b; - b = temp; - } - - GenericGFPoly rLast = a; - GenericGFPoly r = b; - GenericGFPoly tLast = field.getZero(); - GenericGFPoly t = field.getOne(); - - // Run Euclidean algorithm until r's degree is less than R/2 - while (2 * r.getDegree() >= R) { - GenericGFPoly rLastLast = rLast; - GenericGFPoly 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? - throw new ReedSolomonException("r_{i-1} was zero"); - } - r = rLastLast; - GenericGFPoly q = field.getZero(); - int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); - int dltInverse = field.inverse(denominatorLeadingTerm); - while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { - int degreeDiff = r.getDegree() - rLast.getDegree(); - int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); - q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); - r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); - } - - t = q.multiply(tLast).addOrSubtract(tLastLast); - - if (r.getDegree() >= rLast.getDegree()) { - throw new IllegalStateException("Division algorithm failed to reduce polynomial? " + - "r: " + r + ", rLast: " + rLast); - } - } - - int sigmaTildeAtZero = t.getCoefficient(0); - if (sigmaTildeAtZero == 0) { - throw new ReedSolomonException("sigmaTilde(0) was zero"); - } - - int inverse = field.inverse(sigmaTildeAtZero); - GenericGFPoly sigma = t.multiply(inverse); - GenericGFPoly omega = r.multiply(inverse); - return new GenericGFPoly[]{sigma, omega}; - } - - private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException { - // This is a direct application of Chien's search - int numErrors = errorLocator.getDegree(); - if (numErrors == 1) { // shortcut - return new int[] { errorLocator.getCoefficient(1) }; - } - int[] result = new int[numErrors]; - int e = 0; - for (int i = 1; i < field.getSize() && e < numErrors; i++) { - if (errorLocator.evaluateAt(i) == 0) { - result[e] = field.inverse(i); - e++; - } - } - if (e != numErrors) { - throw new ReedSolomonException("Error locator degree does not match number of roots"); - } - return result; - } - - private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations) { - // This is directly applying Forney's Formula - int s = errorLocations.length; - int[] result = new int[s]; - for (int i = 0; i < s; i++) { - int xiInverse = field.inverse(errorLocations[i]); - int denominator = 1; - 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 - int term = field.multiply(errorLocations[j], xiInverse); - int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1; - denominator = field.multiply(denominator, termPlus1); - } - } - result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), - field.inverse(denominator)); - if (field.getGeneratorBase() != 0) { - result[i] = field.multiply(result[i], xiInverse); - } - } - return result; - } - -} diff --git a/src/common/reedsolomon/ReedSolomonEncoder.java b/src/common/reedsolomon/ReedSolomonEncoder.java deleted file mode 100644 index 2e2b2f0..0000000 --- a/src/common/reedsolomon/ReedSolomonEncoder.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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; - -/** - *

Implements Reed-Solomon encoding, as the name implies.

- * - * @author Sean Owen - * @author William Rucklidge - */ -public final class ReedSolomonEncoder { - - private final GenericGF field; - private final List cachedGenerators; - - public ReedSolomonEncoder(GenericGF field) { - this.field = field; - this.cachedGenerators = new ArrayList<>(); - cachedGenerators.add(new GenericGFPoly(field, new int[]{1})); - } - - private GenericGFPoly buildGenerator(int degree) { - if (degree >= cachedGenerators.size()) { - GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1); - for (int d = cachedGenerators.size(); d <= degree; d++) { - GenericGFPoly nextGenerator = lastGenerator.multiply( - new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) })); - cachedGenerators.add(nextGenerator); - lastGenerator = nextGenerator; - } - } - return cachedGenerators.get(degree); - } - - public void encode(int[] toEncode, int ecBytes) { - if (ecBytes == 0) { - throw new IllegalArgumentException("No error correction bytes"); - } - int dataBytes = toEncode.length - ecBytes; - if (dataBytes <= 0) { - throw new IllegalArgumentException("No data bytes provided"); - } - GenericGFPoly generator = buildGenerator(ecBytes); - int[] infoCoefficients = new int[dataBytes]; - System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); - GenericGFPoly info = new GenericGFPoly(field, infoCoefficients); - info = info.multiplyByMonomial(ecBytes, 1); - GenericGFPoly remainder = info.divide(generator)[1]; - int[] coefficients = remainder.getCoefficients(); - int numZeroCoefficients = ecBytes - coefficients.length; - for (int i = 0; i < numZeroCoefficients; i++) { - toEncode[dataBytes + i] = 0; - } - System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); - } - -} diff --git a/src/common/reedsolomon/ReedSolomonException.java b/src/common/reedsolomon/ReedSolomonException.java deleted file mode 100644 index d5b45a6..0000000 --- a/src/common/reedsolomon/ReedSolomonException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

Thrown when an exception occurs during Reed-Solomon decoding, such as when - * there are too many errors to correct.

- * - * @author Sean Owen - */ -public final class ReedSolomonException extends Exception { - - public ReedSolomonException(String message) { - super(message); - } - -} \ No newline at end of file diff --git a/src/common/reedsolomon/mod.rs b/src/common/reedsolomon/mod.rs index e69de29..501faf6 100644 --- a/src/common/reedsolomon/mod.rs +++ b/src/common/reedsolomon/mod.rs @@ -0,0 +1,744 @@ +/* + * 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; + +/** + *

Thrown when an exception occurs during Reed-Solomon decoding, such as when + * there are too many errors to correct.

+ * + * @author Sean Owen + */ +pub struct ReedSolomonException { + message: String, +} + +impl ReedSolomonException { + pub fn new(message: &str) -> Self { + Self { + message: message.to_owned(), + } + } +} + +/* + * 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; + +/** + *

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 + */ +public final class GenericGF { + + public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 + public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 + public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 + public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 + public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 + public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 + public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256; + public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6; + + private final int[] expTable; + private final int[] logTable; + private final GenericGFPoly zero; + private final GenericGFPoly one; + private final int size; + private final int primitive; + private final int generatorBase; + + /** + * 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. + */ + public GenericGF(int primitive, int size, int b) { + this.primitive = primitive; + this.size = size; + this.generatorBase = b; + + expTable = new int[size]; + logTable = new int[size]; + int x = 1; + for (int i = 0; i < size; i++) { + expTable[i] = x; + x *= 2; // we're assuming the generator alpha is 2 + if (x >= size) { + x ^= primitive; + x &= size - 1; + } + } + for (int i = 0; i < size - 1; i++) { + logTable[expTable[i]] = i; + } + // logTable[0] == 0 but this should never be used + zero = new GenericGFPoly(this, new int[]{0}); + one = new GenericGFPoly(this, new int[]{1}); + } + + GenericGFPoly getZero() { + return zero; + } + + GenericGFPoly getOne() { + return one; + } + + /** + * @return the monomial representing coefficient * x^degree + */ + GenericGFPoly buildMonomial(int degree, int coefficient) { + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return zero; + } + int[] coefficients = new int[degree + 1]; + coefficients[0] = coefficient; + return new GenericGFPoly(this, coefficients); + } + + /** + * Implements both addition and subtraction -- they are the same in GF(size). + * + * @return sum/difference of a and b + */ + static int addOrSubtract(int a, int b) { + return a ^ b; + } + + /** + * @return 2 to the power of a in GF(size) + */ + int exp(int a) { + return expTable[a]; + } + + /** + * @return base 2 log of a in GF(size) + */ + int log(int a) { + if (a == 0) { + throw new IllegalArgumentException(); + } + return logTable[a]; + } + + /** + * @return multiplicative inverse of a + */ + int inverse(int a) { + if (a == 0) { + throw new ArithmeticException(); + } + return expTable[size - logTable[a] - 1]; + } + + /** + * @return product of a and b in GF(size) + */ + int multiply(int a, int b) { + if (a == 0 || b == 0) { + return 0; + } + return expTable[(logTable[a] + logTable[b]) % (size - 1)]; + } + + public int getSize() { + return size; + } + + public int getGeneratorBase() { + return generatorBase; + } + + @Override + public String toString() { + return "GF(0x" + Integer.toHexString(primitive) + ',' + 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 + */ +final class GenericGFPoly { + + private final GenericGF field; + private final int[] coefficients; + + /** + * @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") + */ + GenericGFPoly(GenericGF field, int[] coefficients) { + if (coefficients.length == 0) { + throw new IllegalArgumentException(); + } + this.field = field; + int coefficientsLength = coefficients.length; + if (coefficientsLength > 1 && coefficients[0] == 0) { + // Leading term must be non-zero for anything except the constant polynomial "0" + int firstNonZero = 1; + while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { + firstNonZero++; + } + if (firstNonZero == coefficientsLength) { + this.coefficients = new int[]{0}; + } else { + this.coefficients = new int[coefficientsLength - firstNonZero]; + System.arraycopy(coefficients, + firstNonZero, + this.coefficients, + 0, + this.coefficients.length); + } + } else { + this.coefficients = coefficients; + } + } + + int[] getCoefficients() { + return coefficients; + } + + /** + * @return degree of this polynomial + */ + int getDegree() { + return coefficients.length - 1; + } + + /** + * @return true iff this polynomial is the monomial "0" + */ + boolean isZero() { + return coefficients[0] == 0; + } + + /** + * @return coefficient of x^degree term in this polynomial + */ + int getCoefficient(int degree) { + return coefficients[coefficients.length - 1 - degree]; + } + + /** + * @return evaluation of this polynomial at a given point + */ + int evaluateAt(int a) { + if (a == 0) { + // Just return the x^0 coefficient + return getCoefficient(0); + } + if (a == 1) { + // Just the sum of the coefficients + int result = 0; + for (int coefficient : coefficients) { + result = GenericGF.addOrSubtract(result, coefficient); + } + return result; + } + int result = coefficients[0]; + int size = coefficients.length; + for (int i = 1; i < size; i++) { + result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); + } + return result; + } + + GenericGFPoly addOrSubtract(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero()) { + return other; + } + if (other.isZero()) { + return this; + } + + int[] smallerCoefficients = this.coefficients; + int[] largerCoefficients = other.coefficients; + if (smallerCoefficients.length > largerCoefficients.length) { + int[] temp = smallerCoefficients; + smallerCoefficients = largerCoefficients; + largerCoefficients = temp; + } + int[] sumDiff = new int[largerCoefficients.length]; + int lengthDiff = largerCoefficients.length - smallerCoefficients.length; + // Copy high-order terms only found in higher-degree polynomial's coefficients + System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); + + for (int i = lengthDiff; i < largerCoefficients.length; i++) { + sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); + } + + return new GenericGFPoly(field, sumDiff); + } + + GenericGFPoly multiply(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (isZero() || other.isZero()) { + return field.getZero(); + } + int[] aCoefficients = this.coefficients; + int aLength = aCoefficients.length; + int[] bCoefficients = other.coefficients; + int bLength = bCoefficients.length; + int[] product = new int[aLength + bLength - 1]; + for (int i = 0; i < aLength; i++) { + int aCoeff = aCoefficients[i]; + for (int j = 0; j < bLength; j++) { + product[i + j] = GenericGF.addOrSubtract(product[i + j], + field.multiply(aCoeff, bCoefficients[j])); + } + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly multiply(int scalar) { + if (scalar == 0) { + return field.getZero(); + } + if (scalar == 1) { + return this; + } + int size = coefficients.length; + int[] product = new int[size]; + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], scalar); + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly multiplyByMonomial(int degree, int coefficient) { + if (degree < 0) { + throw new IllegalArgumentException(); + } + if (coefficient == 0) { + return field.getZero(); + } + int size = coefficients.length; + int[] product = new int[size + degree]; + for (int i = 0; i < size; i++) { + product[i] = field.multiply(coefficients[i], coefficient); + } + return new GenericGFPoly(field, product); + } + + GenericGFPoly[] divide(GenericGFPoly other) { + if (!field.equals(other.field)) { + throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); + } + if (other.isZero()) { + throw new IllegalArgumentException("Divide by 0"); + } + + GenericGFPoly quotient = field.getZero(); + GenericGFPoly remainder = this; + + int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); + int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); + + while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { + int degreeDifference = remainder.getDegree() - other.getDegree(); + int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); + GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale); + GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale); + quotient = quotient.addOrSubtract(iterationQuotient); + remainder = remainder.addOrSubtract(term); + } + + return new GenericGFPoly[] { quotient, remainder }; + } + + @Override + public String toString() { + if (isZero()) { + return "0"; + } + StringBuilder result = new StringBuilder(8 * getDegree()); + for (int degree = getDegree(); degree >= 0; degree--) { + int coefficient = getCoefficient(degree); + if (coefficient != 0) { + if (coefficient < 0) { + if (degree == getDegree()) { + result.append("-"); + } else { + result.append(" - "); + } + coefficient = -coefficient; + } else { + if (result.length() > 0) { + result.append(" + "); + } + } + if (degree == 0 || coefficient != 1) { + int alphaPower = field.log(coefficient); + if (alphaPower == 0) { + result.append('1'); + } else if (alphaPower == 1) { + result.append('a'); + } else { + result.append("a^"); + result.append(alphaPower); + } + } + if (degree != 0) { + if (degree == 1) { + result.append('x'); + } else { + result.append("x^"); + result.append(degree); + } + } + } + } + return result.toString(); + } + +} + + +/* + * 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 + */ +public final class ReedSolomonDecoder { + + private final GenericGF field; + + public ReedSolomonDecoder(GenericGF field) { + this.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 + */ + public void decode(int[] received, int twoS) throws ReedSolomonException { + GenericGFPoly poly = new GenericGFPoly(field, received); + int[] syndromeCoefficients = new int[twoS]; + boolean noError = true; + for (int i = 0; i < twoS; i++) { + int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase())); + syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; + if (eval != 0) { + noError = false; + } + } + if (noError) { + return; + } + GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients); + GenericGFPoly[] sigmaOmega = + runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); + GenericGFPoly sigma = sigmaOmega[0]; + GenericGFPoly omega = sigmaOmega[1]; + int[] errorLocations = findErrorLocations(sigma); + int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations); + for (int i = 0; i < errorLocations.length; i++) { + int position = received.length - 1 - field.log(errorLocations[i]); + if (position < 0) { + throw new ReedSolomonException("Bad error location"); + } + received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]); + } + } + + private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R) + throws ReedSolomonException { + // Assume a's degree is >= b's + if (a.getDegree() < b.getDegree()) { + GenericGFPoly temp = a; + a = b; + b = temp; + } + + GenericGFPoly rLast = a; + GenericGFPoly r = b; + GenericGFPoly tLast = field.getZero(); + GenericGFPoly t = field.getOne(); + + // Run Euclidean algorithm until r's degree is less than R/2 + while (2 * r.getDegree() >= R) { + GenericGFPoly rLastLast = rLast; + GenericGFPoly 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? + throw new ReedSolomonException("r_{i-1} was zero"); + } + r = rLastLast; + GenericGFPoly q = field.getZero(); + int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); + int dltInverse = field.inverse(denominatorLeadingTerm); + while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { + int degreeDiff = r.getDegree() - rLast.getDegree(); + int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); + q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); + r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); + } + + t = q.multiply(tLast).addOrSubtract(tLastLast); + + if (r.getDegree() >= rLast.getDegree()) { + throw new IllegalStateException("Division algorithm failed to reduce polynomial? " + + "r: " + r + ", rLast: " + rLast); + } + } + + int sigmaTildeAtZero = t.getCoefficient(0); + if (sigmaTildeAtZero == 0) { + throw new ReedSolomonException("sigmaTilde(0) was zero"); + } + + int inverse = field.inverse(sigmaTildeAtZero); + GenericGFPoly sigma = t.multiply(inverse); + GenericGFPoly omega = r.multiply(inverse); + return new GenericGFPoly[]{sigma, omega}; + } + + private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException { + // This is a direct application of Chien's search + int numErrors = errorLocator.getDegree(); + if (numErrors == 1) { // shortcut + return new int[] { errorLocator.getCoefficient(1) }; + } + int[] result = new int[numErrors]; + int e = 0; + for (int i = 1; i < field.getSize() && e < numErrors; i++) { + if (errorLocator.evaluateAt(i) == 0) { + result[e] = field.inverse(i); + e++; + } + } + if (e != numErrors) { + throw new ReedSolomonException("Error locator degree does not match number of roots"); + } + return result; + } + + private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations) { + // This is directly applying Forney's Formula + int s = errorLocations.length; + int[] result = new int[s]; + for (int i = 0; i < s; i++) { + int xiInverse = field.inverse(errorLocations[i]); + int denominator = 1; + 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 + int term = field.multiply(errorLocations[j], xiInverse); + int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1; + denominator = field.multiply(denominator, termPlus1); + } + } + result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), + field.inverse(denominator)); + if (field.getGeneratorBase() != 0) { + result[i] = 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; + +/** + *

Implements Reed-Solomon encoding, as the name implies.

+ * + * @author Sean Owen + * @author William Rucklidge + */ +public final class ReedSolomonEncoder { + + private final GenericGF field; + private final List cachedGenerators; + + public ReedSolomonEncoder(GenericGF field) { + this.field = field; + this.cachedGenerators = new ArrayList<>(); + cachedGenerators.add(new GenericGFPoly(field, new int[]{1})); + } + + private GenericGFPoly buildGenerator(int degree) { + if (degree >= cachedGenerators.size()) { + GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1); + for (int d = cachedGenerators.size(); d <= degree; d++) { + GenericGFPoly nextGenerator = lastGenerator.multiply( + new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) })); + cachedGenerators.add(nextGenerator); + lastGenerator = nextGenerator; + } + } + return cachedGenerators.get(degree); + } + + public void encode(int[] toEncode, int ecBytes) { + if (ecBytes == 0) { + throw new IllegalArgumentException("No error correction bytes"); + } + int dataBytes = toEncode.length - ecBytes; + if (dataBytes <= 0) { + throw new IllegalArgumentException("No data bytes provided"); + } + GenericGFPoly generator = buildGenerator(ecBytes); + int[] infoCoefficients = new int[dataBytes]; + System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); + GenericGFPoly info = new GenericGFPoly(field, infoCoefficients); + info = info.multiplyByMonomial(ecBytes, 1); + GenericGFPoly remainder = info.divide(generator)[1]; + int[] coefficients = remainder.getCoefficients(); + int numZeroCoefficients = ecBytes - coefficients.length; + for (int i = 0; i < numZeroCoefficients; i++) { + toEncode[dataBytes + i] = 0; + } + System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); + } + +} diff --git a/src/lib.rs b/src/lib.rs index cee91dc..61b25fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,143 @@ -mod common; \ No newline at end of file +mod common; + +/* + * 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; + +/** + * Thrown when a barcode was not found in the image. It might have been + * partially detected but could not be confirmed. + * + * @author Sean Owen + */ +pub struct NotFoundException; + +/* + * 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; + +/** + * Thrown when a barcode was successfully detected, but some aspect of + * the content did not conform to the barcode's format rules. This could have + * been due to a mis-detection. + * + * @author Sean Owen + */ +pub struct FormatException; + +/* + * 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; + +/** + * Thrown when a barcode was successfully detected and decoded, but + * was not returned because its checksum feature failed. + * + * @author Sean Owen + */ +pub struct ChecksumException; + +/* + * 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; + +/** + * The general exception class throw when something goes wrong during decoding of a barcode. + * This includes, but is not limited to, failing checksums / error correction algorithms, being + * unable to locate finder timing patterns, and so on. + * + * @author Sean Owen + */ +pub struct ReaderException; + +/* + * 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; + +/** + * A base class which covers the range of exceptions which may occur when encoding a barcode using + * the Writer framework. + * + * @author dswitkin@google.com (Daniel Switkin) + */ +pub struct WriterException { + message: String, +} + +impl WriterException { + pub fn new(message: &str) -> Self { + Self { + message: message.to_owned(), + } + } +}