mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
progress
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>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.</p>
|
|
||||||
*
|
|
||||||
* @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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
|
||||||
* white, in an image.</p>
|
|
||||||
*
|
|
||||||
* @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<Vec<RXingResultPoint>, 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<RXingResultPoint, NotFoundException> {
|
|
||||||
let lastRange : Option<Vec<i32>> = None;
|
|
||||||
let y: i32 = centerY;
|
|
||||||
let x: i32 = centerX;
|
|
||||||
while (y < bottom && y >= top && x < right && x >= left) {
|
|
||||||
let range: Option<Vec<i32>>;
|
|
||||||
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<Vec<i32>> {
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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,NotFoundException> {
|
|
||||||
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<Self, NotFoundException> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* 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.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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<Vec<RXingResultPoint>, 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> {
|
|
||||||
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<RXingResultPoint> {
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,666 @@
|
|||||||
mod MonochromeRectangleDetector;
|
|
||||||
mod WhiteRectangleDetector;
|
|
||||||
|
|
||||||
pub mod MathUtils;
|
pub mod MathUtils;
|
||||||
|
|
||||||
pub use MonochromeRectangleDetector::*;
|
/*
|
||||||
pub use WhiteRectangleDetector::*;
|
* 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};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* @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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||||
|
* white, in an image.</p>
|
||||||
|
*
|
||||||
|
* @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<Vec<RXingResultPoint>, 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<RXingResultPoint, NotFoundException> {
|
||||||
|
let lastRange: Option<Vec<i32>> = None;
|
||||||
|
let y: i32 = centerY;
|
||||||
|
let x: i32 = centerX;
|
||||||
|
while (y < bottom && y >= top && x < right && x >= left) {
|
||||||
|
let range: Option<Vec<i32>>;
|
||||||
|
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<Vec<i32>> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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, NotFoundException> {
|
||||||
|
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<Self, NotFoundException> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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<Vec<RXingResultPoint>, 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> = 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<RXingResultPoint> {
|
||||||
|
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<RXingResultPoint> {
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>This class contains utility methods for performing mathematical operations over
|
|
||||||
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
|
|
||||||
*
|
|
||||||
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
|
||||||
* for convenience and speed (but at the cost of memory).
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @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 + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
|
||||||
* Instances of this class are immutable.</p>
|
|
||||||
*
|
|
||||||
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
|
||||||
* port of his C++ Reed-Solomon implementation.</p>
|
|
||||||
*
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
|
|
||||||
*
|
|
||||||
* <p>The algorithm will not be explained here, but the following references were helpful
|
|
||||||
* in creating this implementation:</p>
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>Bruce Maggs.
|
|
||||||
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
|
|
||||||
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
|
|
||||||
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
|
|
||||||
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
|
|
||||||
* (see discussion of Euclidean algorithm)</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
|
||||||
* port of his C++ Reed-Solomon implementation.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author William Rucklidge
|
|
||||||
* @author sanfordsquires
|
|
||||||
*/
|
|
||||||
public final class ReedSolomonDecoder {
|
|
||||||
|
|
||||||
private final GenericGF field;
|
|
||||||
|
|
||||||
public ReedSolomonDecoder(GenericGF field) {
|
|
||||||
this.field = field;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>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.</p>
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Implements Reed-Solomon encoding, as the name implies.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
* @author William Rucklidge
|
|
||||||
*/
|
|
||||||
public final class ReedSolomonEncoder {
|
|
||||||
|
|
||||||
private final GenericGF field;
|
|
||||||
private final List<GenericGFPoly> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
|
|
||||||
* there are too many errors to correct.</p>
|
|
||||||
*
|
|
||||||
* @author Sean Owen
|
|
||||||
*/
|
|
||||||
public final class ReedSolomonException extends Exception {
|
|
||||||
|
|
||||||
public ReedSolomonException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
|
||||||
|
* there are too many errors to correct.</p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>This class contains utility methods for performing mathematical operations over
|
||||||
|
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
|
||||||
|
*
|
||||||
|
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
||||||
|
* for convenience and speed (but at the cost of memory).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
||||||
|
* Instances of this class are immutable.</p>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
|
||||||
|
*
|
||||||
|
* <p>The algorithm will not be explained here, but the following references were helpful
|
||||||
|
* in creating this implementation:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Bruce Maggs.
|
||||||
|
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
|
||||||
|
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
|
||||||
|
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
|
||||||
|
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
|
||||||
|
* (see discussion of Euclidean algorithm)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||||
|
* port of his C++ Reed-Solomon implementation.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author William Rucklidge
|
||||||
|
* @author sanfordsquires
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonDecoder {
|
||||||
|
|
||||||
|
private final GenericGF field;
|
||||||
|
|
||||||
|
public ReedSolomonDecoder(GenericGF field) {
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implements Reed-Solomon encoding, as the name implies.</p>
|
||||||
|
*
|
||||||
|
* @author Sean Owen
|
||||||
|
* @author William Rucklidge
|
||||||
|
*/
|
||||||
|
public final class ReedSolomonEncoder {
|
||||||
|
|
||||||
|
private final GenericGF field;
|
||||||
|
private final List<GenericGFPoly> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
144
src/lib.rs
144
src/lib.rs
@@ -1 +1,143 @@
|
|||||||
mod common;
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user