mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
removed for rebuild
This commit is contained in:
285
src/aztec.rs
285
src/aztec.rs
@@ -1,285 +0,0 @@
|
||||
pub mod decoder;
|
||||
pub mod detector;
|
||||
pub mod encoder;
|
||||
|
||||
use crate::aztec::decoder::Decoder;
|
||||
use crate::aztec::detector::Detector;
|
||||
use crate::common::{BitMatrix, DecoderResult, DetectorResult};
|
||||
use crate::{
|
||||
BarcodeFormat, BinaryBitmap, DecodeHintType, FormatException, NotFoundException, Reader,
|
||||
Result, ResultMetadataType, ResultPoint, ResultPointCallback,
|
||||
};
|
||||
use crate::{
|
||||
BarcodeFormat, EncodeHintType, Reader, ReaderException, ResultPoint, Writer, WriterException,
|
||||
};
|
||||
|
||||
use crate::aztec::encoder::{AztecCode, Encoder};
|
||||
|
||||
// AztecDetectorResult.java
|
||||
/**
|
||||
* <p>Extends {@link DetectorResult} with more information specific to the Aztec format,
|
||||
* like the number of layers and whether it's compact.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
pub struct AztecDetectorResult {
|
||||
//super: DetectorResult;
|
||||
compact: bool,
|
||||
|
||||
nb_datablocks: i32,
|
||||
|
||||
nb_layers: i32,
|
||||
|
||||
bits: BitMatrix,
|
||||
|
||||
points: Vec<ResultPoint>,
|
||||
}
|
||||
|
||||
impl DetectorResult for AztecDetectorResult {
|
||||
fn get_bits(&self) -> BitMatrix {
|
||||
return self.bits;
|
||||
}
|
||||
|
||||
fn get_points(&self) -> Vec<ResultPoint> {
|
||||
return self.points;
|
||||
}
|
||||
}
|
||||
|
||||
impl AztecDetectorResult {
|
||||
pub fn new(
|
||||
bits: &BitMatrix,
|
||||
points: &Vec<ResultPoint>,
|
||||
compact: bool,
|
||||
nb_datablocks: i32,
|
||||
nb_layers: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
compact: compact,
|
||||
nb_datablocks: nd_datablocks,
|
||||
nb_layers: nb_layers,
|
||||
bits: bits,
|
||||
points: points,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_nb_layers(&self) -> i32 {
|
||||
return self.nb_layers;
|
||||
}
|
||||
|
||||
pub fn get_nb_datablocks(&self) -> i32 {
|
||||
return self.nb_datablocks;
|
||||
}
|
||||
|
||||
pub fn is_compact(&self) -> bool {
|
||||
return self.compact;
|
||||
}
|
||||
}
|
||||
|
||||
// AztecReader.java
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode Aztec codes in an image.
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
pub struct AztecReader {}
|
||||
|
||||
impl Reader for AztecReader {
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
*/
|
||||
/*fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, FormatException */Result<Result, Rc<Exception>> {
|
||||
return Ok(self.decode(image, null));
|
||||
}*/
|
||||
|
||||
fn decode(
|
||||
&self,
|
||||
image: &BinaryBitmap,
|
||||
hints: &Map<DecodeHintType, _>,
|
||||
) -> Result<Result, ReaderException> {
|
||||
let not_found_exception: NotFoundException = null;
|
||||
let format_exception: FormatException = null;
|
||||
let detector: Detector = Detector::new(&image.get_black_matrix());
|
||||
let mut points: Vec<ResultPoint> = null;
|
||||
let decoder_result: DecoderResult = null;
|
||||
|
||||
let detector_result: AztecDetectorResult = detector.detect(Some(false))?;
|
||||
points = detector_result.get_points()?;
|
||||
decoder_result = Decoder::new().decode(detector_result);
|
||||
/*
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let detector_result: AztecDetectorResult = detector.detect(Some(false));
|
||||
points = detector_result.get_points();
|
||||
decoder_result = Decoder::new().decode(detector_result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( e: &NotFoundException) {
|
||||
not_found_exception = e;
|
||||
} catch ( e: &FormatException) {
|
||||
format_exception = e;
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
if decoder_result == null {
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let detector_result: AztecDetectorResult = detector.detect(true);
|
||||
points = detector_result.get_points();
|
||||
decoder_result = Decoder::new().decode(detector_result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( e: &NotFoundExceptionFormatException | ) {
|
||||
if not_found_exception != null {
|
||||
throw not_found_exception;
|
||||
}
|
||||
if format_exception != null {
|
||||
throw format_exception;
|
||||
}
|
||||
throw e;
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
if hints != null {
|
||||
let rpcb: ResultPointCallback =
|
||||
hints.get(DecodeHintType::NEED_RESULT_POINT_CALLBACK) as ResultPointCallback;
|
||||
if rpcb != null {
|
||||
for point in points {
|
||||
rpcb.found_possible_result_point(&point);
|
||||
}
|
||||
}
|
||||
}
|
||||
let result: Result = Result::new(
|
||||
&decoder_result.get_text(),
|
||||
&decoder_result.get_raw_bytes(),
|
||||
&decoder_result.get_num_bits(),
|
||||
points,
|
||||
BarcodeFormat::AZTEC,
|
||||
&System::current_time_millis(),
|
||||
);
|
||||
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
||||
if byte_segments != null {
|
||||
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
||||
}
|
||||
let ec_level: String = decoder_result.get_e_c_level();
|
||||
if ec_level != null {
|
||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||
}
|
||||
result.put_metadata(
|
||||
ResultMetadataType::SYMBOLOGY_IDENTIFIER,
|
||||
format!("]z{}", decoder_result.get_symbology_modifier()),
|
||||
);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// AztecWriter.java
|
||||
|
||||
/**
|
||||
* Renders an Aztec code as a {@link BitMatrix}.
|
||||
*/
|
||||
pub struct AztecWriter {}
|
||||
|
||||
impl Writer for AztecWriter {
|
||||
fn encode(
|
||||
&self,
|
||||
contents: &String,
|
||||
format: &BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
hints: Option<&HashMap<EncodeHintType, _>>,
|
||||
) -> BitMatrix {
|
||||
// Do not add any ECI code by default
|
||||
let mut charset: Charset = null;
|
||||
let ecc_percent: i32 = Encoder::DEFAULT_EC_PERCENT;
|
||||
let mut layers: i32 = Encoder::DEFAULT_AZTEC_LAYERS;
|
||||
if hints != null {
|
||||
if hints.contains_key(EncodeHintType::CHARACTER_SET) {
|
||||
charset = Charset::for_name(&hints.get(EncodeHintType::CHARACTER_SET).to_string());
|
||||
}
|
||||
if hints.contains_key(EncodeHintType::ERROR_CORRECTION) {
|
||||
ecc_percent =
|
||||
Integer::parse_int(&hints.get(EncodeHintType::ERROR_CORRECTION).to_string());
|
||||
}
|
||||
if hints.contains_key(EncodeHintType::AZTEC_LAYERS) {
|
||||
layers = Integer::parse_int(&hints.get(EncodeHintType::AZTEC_LAYERS).to_string());
|
||||
}
|
||||
}
|
||||
return ::encode(
|
||||
&contents,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
&charset,
|
||||
ecc_percent,
|
||||
layers,
|
||||
);
|
||||
}
|
||||
/*
|
||||
fn encode( contents: &String, format: &BarcodeFormat, width: i32, height: i32, charset: &Charset, ecc_percent: i32, layers: i32) -> BitMatrix {
|
||||
if format != BarcodeFormat::AZTEC {
|
||||
return Err( IllegalArgumentException::new(format!("Can only encode AZTEC, but got {}", format)));
|
||||
}
|
||||
let aztec: AztecCode = Encoder::encode(&contents, ecc_percent, layers, &charset);
|
||||
return ::render_result(aztec, width, height);
|
||||
}*/
|
||||
}
|
||||
|
||||
impl AztecWriter {
|
||||
fn render_result(code: &AztecCode, width: i32, height: i32) -> BitMatrix {
|
||||
let input: BitMatrix = code.get_matrix();
|
||||
if input == null {
|
||||
return Err(IllegalStateException::new());
|
||||
}
|
||||
let input_width: i32 = input.get_width();
|
||||
let input_height: i32 = input.get_height();
|
||||
let output_width: i32 = Math::max(width, input_width);
|
||||
let output_height: i32 = Math::max(height, input_height);
|
||||
let multiple: i32 = Math::min(output_width / input_width, output_height / input_height);
|
||||
let left_padding: i32 = (output_width - (input_width * multiple)) / 2;
|
||||
let top_padding: i32 = (output_height - (input_height * multiple)) / 2;
|
||||
let output: BitMatrix = BitMatrix::new(output_width, output_height);
|
||||
{
|
||||
let input_y: i32 = 0;
|
||||
let output_y: i32 = top_padding;
|
||||
while input_y < input_height {
|
||||
{
|
||||
// Write the contents of this row of the barcode
|
||||
{
|
||||
let input_x: i32 = 0;
|
||||
let output_x: i32 = left_padding;
|
||||
while input_x < input_width {
|
||||
{
|
||||
if input.get(input_x, input_y) {
|
||||
output.set_region(output_x, output_y, multiple, multiple);
|
||||
}
|
||||
}
|
||||
input_x += 1;
|
||||
output_x += multiple;
|
||||
}
|
||||
}
|
||||
}
|
||||
input_y += 1;
|
||||
output_y += multiple;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -1,548 +0,0 @@
|
||||
use crate::aztec::AztecDetectorResult;
|
||||
use crate::common::reedsolomon::{GenericGF, ReedSolomonDecoder, ReedSolomonException};
|
||||
use crate::common::{BitMatrix, CharacterSetECI, DecoderResult};
|
||||
use create::FormatException;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting
|
||||
* the Aztec Code from an image.</p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
|
||||
const UPPER_TABLE: vec![Vec<String>; 32] = vec![
|
||||
"CTRL_PS", " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS",
|
||||
];
|
||||
|
||||
const LOWER_TABLE: vec![Vec<String>; 32] = vec![
|
||||
"CTRL_PS", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
|
||||
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS",
|
||||
];
|
||||
|
||||
const MIXED_TABLE: vec![Vec<String>; 32] = vec![
|
||||
"CTRL_PS", " ", "\u{0001}", "\u{0002}", "\u{0003}", "\u{0004}", "\u{0005}", "\u{0006}",
|
||||
"\u{0007}", "\u{000b}", "\t", "\n", "\u{000d}", "\u{000f}", "\r", "\u{0021}", "\u{0022}",
|
||||
"\u{0023}", "\u{0024}", "\u{0025}", "@", "\\", "^", "_", "`", "|", "~", "\u{00b1}", "CTRL_LL",
|
||||
"CTRL_UL", "CTRL_PL", "CTRL_BS",
|
||||
];
|
||||
|
||||
const PUNCT_TABLE: vec![Vec<String>; 32] = vec![
|
||||
"FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*",
|
||||
"+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL",
|
||||
];
|
||||
|
||||
const DIGIT_TABLE: vec![Vec<String>; 16] = vec![
|
||||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL",
|
||||
"CTRL_US",
|
||||
];
|
||||
|
||||
const DEFAULT_ENCODING: Charset = StandardCharsets::ISO_8859_1;
|
||||
pub struct Decoder {
|
||||
ddata: AztecDetectorResult,
|
||||
}
|
||||
|
||||
enum Table {
|
||||
UPPER(),
|
||||
LOWER(),
|
||||
MIXED(),
|
||||
DIGIT(),
|
||||
PUNCT(),
|
||||
BINARY(),
|
||||
}
|
||||
|
||||
impl Decoder {
|
||||
pub fn decode(
|
||||
&self,
|
||||
detector_result: &AztecDetectorResult,
|
||||
) -> Result<DecoderResult, FormatException> {
|
||||
self.ddata = detector_result;
|
||||
let matrix: BitMatrix = detector_result.get_bits();
|
||||
let rawbits: Vec<bool> = self.extract_bits(&matrix);
|
||||
let corrected_bits: CorrectedBitsResult = self.correct_bits(&rawbits);
|
||||
let raw_bytes: Vec<i8> = ::convert_bool_array_to_byte_array(corrected_bits.correctBits);
|
||||
let result: String = ::get_encoded_data(corrected_bits.correctBits);
|
||||
let decoder_result: DecoderResult = DecoderResult::new(
|
||||
&raw_bytes,
|
||||
&result,
|
||||
null,
|
||||
&String::format("%d%%", corrected_bits.ecLevel),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
decoder_result.set_num_bits(corrected_bits.correctBits.len());
|
||||
return Ok(decoder_result);
|
||||
}
|
||||
|
||||
// This method is used for testing the high-level encoder
|
||||
pub fn high_level_decode(corrected_bits: &Vec<bool>) -> Result<String, Rc<Exception>> {
|
||||
return Ok(::get_encoded_data(&corrected_bits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string encoded in the aztec code bits
|
||||
*
|
||||
* @return the decoded string
|
||||
*/
|
||||
fn get_encoded_data(corrected_bits: &Vec<bool>) -> Result<String, Rc<Exception>> {
|
||||
let end_index: i32 = corrected_bits.len();
|
||||
// table most recently latched to
|
||||
let latch_table: Table = Table::UPPER;
|
||||
// table to use for the next read
|
||||
let shift_table: Table = Table::UPPER;
|
||||
// Final decoded string result
|
||||
// (correctedBits-5) / 4 is an upper bound on the size (all-digit result)
|
||||
let result: StringBuilder = StringBuilder::new((corrected_bits.len() - 5) / 4);
|
||||
// Intermediary buffer of decoded bytes, which is decoded into a string and flushed
|
||||
// when character encoding changes (ECI) or input ends.
|
||||
let decoded_bytes: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
||||
let mut encoding: Charset = DEFAULT_ENCODING;
|
||||
let mut index: i32 = 0;
|
||||
while index < end_index {
|
||||
if shift_table == Table::BINARY {
|
||||
if end_index - index < 5 {
|
||||
break;
|
||||
}
|
||||
let mut length: i32 = ::read_code(&corrected_bits, index, 5);
|
||||
index += 5;
|
||||
if length == 0 {
|
||||
if end_index - index < 11 {
|
||||
break;
|
||||
}
|
||||
length = ::read_code(&corrected_bits, index, 11) + 31;
|
||||
index += 11;
|
||||
}
|
||||
{
|
||||
let char_count: i32 = 0;
|
||||
while char_count < length {
|
||||
{
|
||||
if end_index - index < 8 {
|
||||
// Force outer loop to exit
|
||||
index = end_index;
|
||||
break;
|
||||
}
|
||||
let code: i32 = ::read_code(&corrected_bits, index, 8);
|
||||
decoded_bytes.write(code as i8);
|
||||
index += 8;
|
||||
}
|
||||
char_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Go back to whatever mode we had been in
|
||||
shift_table = latch_table;
|
||||
} else {
|
||||
let size: i32 = if shift_table == Table::DIGIT { 4 } else { 5 };
|
||||
if end_index - index < size {
|
||||
break;
|
||||
}
|
||||
let code: i32 = ::read_code(&corrected_bits, index, size);
|
||||
index += size;
|
||||
let str: String = ::get_character(shift_table, code);
|
||||
if "FLG(n)".equals(&str) {
|
||||
if end_index - index < 3 {
|
||||
break;
|
||||
}
|
||||
let mut n: i32 = ::read_code(&corrected_bits, index, 3);
|
||||
index += 3;
|
||||
// flush bytes, FLG changes state
|
||||
let tryResult1 = 0;
|
||||
|
||||
result.append(&decoded_bytes.to_string(&encoding.name()));
|
||||
|
||||
decoded_bytes.reset();
|
||||
match n {
|
||||
0 => {
|
||||
// translate FNC1 as ASCII 29
|
||||
result.append(29 as char);
|
||||
break;
|
||||
}
|
||||
7 => {
|
||||
// FLG(7) is reserved and illegal
|
||||
return Err(FormatException::get_format_instance());
|
||||
}
|
||||
_ => {
|
||||
// ECI is decimal integer encoded as 1-6 codes in DIGIT mode
|
||||
let mut eci: i32 = 0;
|
||||
if end_index - index < 4 * n {
|
||||
break;
|
||||
}
|
||||
while (n -= 1) > 0 {
|
||||
let next_digit: i32 = ::read_code(&corrected_bits, index, 4);
|
||||
index += 4;
|
||||
if next_digit < 2 || next_digit > 11 {
|
||||
// Not a decimal digit
|
||||
return Err(FormatException::get_format_instance());
|
||||
}
|
||||
eci = eci * 10 + (next_digit - 2);
|
||||
}
|
||||
let charset_e_c_i: CharacterSetECI =
|
||||
CharacterSetECI::get_character_set_e_c_i_by_value(eci);
|
||||
if charset_e_c_i == null {
|
||||
return Err(FormatException::get_format_instance());
|
||||
}
|
||||
encoding = charset_e_c_i.get_charset();
|
||||
}
|
||||
}
|
||||
// Go back to whatever mode we had been in
|
||||
shift_table = latch_table;
|
||||
} else if str.starts_with("CTRL_") {
|
||||
// Table changes
|
||||
// ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked.
|
||||
// That's including when that mode is a shift.
|
||||
// Our test case dlusbs.png for issue #642 exercises that.
|
||||
// Latch the current mode, so as to return to Upper after U/S B/S
|
||||
latch_table = shift_table;
|
||||
shift_table = ::get_table(&str.char_at(5));
|
||||
if str.char_at(6) == 'L' {
|
||||
latch_table = shift_table;
|
||||
}
|
||||
} else {
|
||||
// Though stored as a table of strings for convenience, codes actually represent 1 or 2 *bytes*.
|
||||
let b: Vec<i8> = str.get_bytes(StandardCharsets::US_ASCII);
|
||||
decoded_bytes.write(&b, 0, b.len());
|
||||
// Go back to whatever mode we had been in
|
||||
shift_table = latch_table;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.append(&decoded_bytes.to_string(&encoding.name()));
|
||||
|
||||
return Ok(result.to_string());
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the table corresponding to the char passed
|
||||
*/
|
||||
fn get_table(t: char) -> Table {
|
||||
match t {
|
||||
'L' => {
|
||||
return Table::LOWER;
|
||||
}
|
||||
'P' => {
|
||||
return Table::PUNCT;
|
||||
}
|
||||
'M' => {
|
||||
return Table::MIXED;
|
||||
}
|
||||
'D' => {
|
||||
return Table::DIGIT;
|
||||
}
|
||||
'B' => {
|
||||
return Table::BINARY;
|
||||
}
|
||||
'U' => {}
|
||||
_ => {
|
||||
return Table::UPPER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character (or string) corresponding to the passed code in the given table
|
||||
*
|
||||
* @param table the table used
|
||||
* @param code the code of the character
|
||||
*/
|
||||
fn get_character(table: &Table, code: i32) -> String {
|
||||
match table {
|
||||
UPPER => {
|
||||
return UPPER_TABLE[code];
|
||||
}
|
||||
LOWER => {
|
||||
return LOWER_TABLE[code];
|
||||
}
|
||||
MIXED => {
|
||||
return MIXED_TABLE[code];
|
||||
}
|
||||
PUNCT => {
|
||||
return PUNCT_TABLE[code];
|
||||
}
|
||||
DIGIT => {
|
||||
return DIGIT_TABLE[code];
|
||||
}
|
||||
_ => {
|
||||
// Should not reach here.
|
||||
return Err(IllegalStateException::new("Bad table"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Performs RS error correction on an array of bits.</p>
|
||||
*
|
||||
* @return the corrected array
|
||||
* @throws FormatException if the input contains too many errors
|
||||
*/
|
||||
fn correct_bits(&self, rawbits: &Vec<bool>) -> Result<CorrectedBitsResult, FormatException> {
|
||||
let mut gf: GenericGF;
|
||||
let codeword_size: i32;
|
||||
if self.ddata.get_nb_layers() <= 2 {
|
||||
codeword_size = 6;
|
||||
gf = GenericGF::AZTEC_DATA_6;
|
||||
} else if self.ddata.get_nb_layers() <= 8 {
|
||||
codeword_size = 8;
|
||||
gf = GenericGF::AZTEC_DATA_8;
|
||||
} else if self.ddata.get_nb_layers() <= 22 {
|
||||
codeword_size = 10;
|
||||
gf = GenericGF::AZTEC_DATA_10;
|
||||
} else {
|
||||
codeword_size = 12;
|
||||
gf = GenericGF::AZTEC_DATA_12;
|
||||
}
|
||||
let num_data_codewords: i32 = self.ddata.get_nb_datablocks();
|
||||
let num_codewords: i32 = rawbits.len() / codeword_size;
|
||||
if num_codewords < num_data_codewords {
|
||||
return Err(FormatException::get_format_instance());
|
||||
}
|
||||
let mut offset: i32 = rawbits.len() % codeword_size;
|
||||
let data_words: [i32; num_codewords] = [0; num_codewords];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_codewords {
|
||||
{
|
||||
data_words[i] = ::read_code(&rawbits, offset, codeword_size);
|
||||
}
|
||||
i += 1;
|
||||
offset += codeword_size;
|
||||
}
|
||||
}
|
||||
|
||||
let tryResult1 = 0;
|
||||
|
||||
let rs_decoder: ReedSolomonDecoder = ReedSolomonDecoder::new(gf)?;
|
||||
rs_decoder.decode(&data_words, num_codewords - num_data_codewords);
|
||||
|
||||
// Now perform the unstuffing operation.
|
||||
// First, count how many bits are going to be thrown out as stuffing
|
||||
let mask: i32 = (1 << codeword_size) - 1;
|
||||
let stuffed_bits: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_data_codewords {
|
||||
{
|
||||
let data_word: i32 = data_words[i];
|
||||
if data_word == 0 || data_word == mask {
|
||||
return Err(FormatException::get_format_instance());
|
||||
} else if data_word == 1 || data_word == mask - 1 {
|
||||
stuffed_bits += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, actually unpack the bits and remove the stuffing
|
||||
let corrected_bits: [bool; num_data_codewords * codeword_size - stuffed_bits] =
|
||||
[false; num_data_codewords * codeword_size - stuffed_bits];
|
||||
let mut index: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_data_codewords {
|
||||
{
|
||||
let data_word: i32 = data_words[i];
|
||||
if data_word == 1 || data_word == mask - 1 {
|
||||
// next codewordSize-1 bits are all zeros or all ones
|
||||
Arrays::fill(
|
||||
&corrected_bits,
|
||||
index,
|
||||
index + codeword_size - 1,
|
||||
data_word > 1,
|
||||
);
|
||||
index += codeword_size - 1;
|
||||
} else {
|
||||
{
|
||||
let mut bit: i32 = codeword_size - 1;
|
||||
while bit >= 0 {
|
||||
{
|
||||
corrected_bits[index += 1] = (data_word & (1 << bit)) != 0;
|
||||
}
|
||||
bit -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(CorrectedBitsResult::new(
|
||||
&corrected_bits,
|
||||
100 * (num_codewords - num_data_codewords) / num_codewords,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of bits from an Aztec Code matrix
|
||||
*
|
||||
* @return the array of bits
|
||||
*/
|
||||
fn extract_bits(&self, matrix: &BitMatrix) -> Vec<bool> {
|
||||
let compact: bool = self.ddata.is_compact();
|
||||
let layers: i32 = self.ddata.get_nb_layers();
|
||||
// not including alignment lines
|
||||
let base_matrix_size: i32 = (if compact { 11 } else { 14 }) + layers * 4;
|
||||
let alignment_map: [i32; base_matrix_size] = [0; base_matrix_size];
|
||||
let mut rawbits: [bool; ::total_bits_in_layer(layers, compact)] =
|
||||
[false; ::total_bits_in_layer(layers, compact)];
|
||||
if compact {
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < alignment_map.len() {
|
||||
{
|
||||
alignment_map[i] = i;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let matrix_size: i32 = base_matrix_size + 1 + 2 * ((base_matrix_size / 2 - 1) / 15);
|
||||
let orig_center: i32 = base_matrix_size / 2;
|
||||
let center: i32 = matrix_size / 2;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < orig_center {
|
||||
{
|
||||
let new_offset: i32 = i + i / 15;
|
||||
alignment_map[orig_center - i - 1] = center - new_offset - 1;
|
||||
alignment_map[orig_center + i] = center + new_offset + 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
let row_offset: i32 = 0;
|
||||
while i < layers {
|
||||
{
|
||||
let row_size: i32 = (layers - i) * 4 + (if compact { 9 } else { 12 });
|
||||
// The top-left most point of this layer is <low, low> (not including alignment lines)
|
||||
let low: i32 = i * 2;
|
||||
// The bottom-right most point of this layer is <high, high> (not including alignment lines)
|
||||
let high: i32 = base_matrix_size - 1 - low;
|
||||
// We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < row_size {
|
||||
{
|
||||
let column_offset: i32 = j * 2;
|
||||
{
|
||||
let mut k: i32 = 0;
|
||||
while k < 2 {
|
||||
{
|
||||
// left column
|
||||
rawbits[row_offset + column_offset + k] = matrix.get(
|
||||
alignment_map[low + k],
|
||||
alignment_map[low + j],
|
||||
);
|
||||
// bottom row
|
||||
rawbits
|
||||
[row_offset + 2 * row_size + column_offset + k] =
|
||||
matrix.get(
|
||||
alignment_map[low + j],
|
||||
alignment_map[high - k],
|
||||
);
|
||||
// right column
|
||||
rawbits
|
||||
[row_offset + 4 * row_size + column_offset + k] =
|
||||
matrix.get(
|
||||
alignment_map[high - k],
|
||||
alignment_map[high - j],
|
||||
);
|
||||
// top row
|
||||
rawbits
|
||||
[row_offset + 6 * row_size + column_offset + k] =
|
||||
matrix.get(
|
||||
alignment_map[high - j],
|
||||
alignment_map[low + k],
|
||||
);
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
row_offset += row_size * 8;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rawbits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a code of given length and at given index in an array of bits
|
||||
*/
|
||||
fn read_code(rawbits: &Vec<bool>, start_index: i32, length: i32) -> i32 {
|
||||
let mut res: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = start_index;
|
||||
while i < start_index + length {
|
||||
{
|
||||
res <<= 1;
|
||||
if rawbits[i] {
|
||||
res |= 0x01;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a code of length 8 in an array of bits, padding with zeros
|
||||
*/
|
||||
fn read_byte(rawbits: &Vec<bool>, start_index: i32) -> i8 {
|
||||
let n: i32 = rawbits.len() - start_index;
|
||||
if n >= 8 {
|
||||
return ::read_code(&rawbits, start_index, 8) as i8;
|
||||
}
|
||||
return (::read_code(&rawbits, start_index, n) << (8 - n)) as i8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs a bit array into bytes, most significant bit first
|
||||
*/
|
||||
fn convert_bool_array_to_byte_array(bool_arr: &Vec<bool>) -> Vec<i8> {
|
||||
let byte_arr: [i8; (bool_arr.len() + 7) / 8] = [0; (bool_arr.len() + 7) / 8];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < byte_arr.len() {
|
||||
{
|
||||
byte_arr[i] = ::read_byte(&bool_arr, 8 * i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return byte_arr;
|
||||
}
|
||||
|
||||
fn total_bits_in_layer(layers: i32, compact: bool) -> i32 {
|
||||
return ((if compact { 88 } else { 112 }) + 16 * layers) * layers;
|
||||
}
|
||||
}
|
||||
|
||||
struct CorrectedBitsResult {
|
||||
correct_bits: Vec<bool>,
|
||||
|
||||
ec_level: i32,
|
||||
}
|
||||
|
||||
impl CorrectedBitsResult {
|
||||
fn new(correct_bits: &Vec<bool>, ec_level: i32) -> Self {
|
||||
Self {
|
||||
correct_bits: correct_bits,
|
||||
ec_level: ec_level,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,670 +0,0 @@
|
||||
use crate::aztec::AztecDetectorResult;
|
||||
use crate::common::detector::{MathUtils, WhiteRectangleDetector};
|
||||
use crate::common::reedsolomon::{GenericGF, ReedSolomonDecoder, ReedSolomonException};
|
||||
use crate::common::{BitMatrix, GridSampler};
|
||||
use crate::{NotFoundException, ResultPoint};
|
||||
|
||||
/**
|
||||
* Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code
|
||||
* is rotated or skewed, or partially obscured.
|
||||
*
|
||||
* @author David Olivier
|
||||
* @author Frank Yellin
|
||||
*/
|
||||
|
||||
const EXPECTED_CORNER_BITS: vec![Vec<i32>; 4] = vec![
|
||||
// 07340 XXX .XX X.. ...
|
||||
0xee0, // 00734 ... XXX .XX X..
|
||||
0x1dc, // 04073 X.. ... XXX .XX
|
||||
0x83b, // 03407 .XX X.. ... XXX
|
||||
0x707,
|
||||
];
|
||||
pub struct Detector {
|
||||
image: BitMatrix,
|
||||
|
||||
compact: bool,
|
||||
|
||||
nb_layers: i32,
|
||||
|
||||
nb_data_blocks: i32,
|
||||
|
||||
nb_center_layers: i32,
|
||||
|
||||
shift: i32,
|
||||
}
|
||||
|
||||
impl Detector {
|
||||
pub fn new(image: &BitMatrix) -> Self {
|
||||
let new_d: Self;
|
||||
new_d.image = image;
|
||||
|
||||
new_d
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects an Aztec Code in an image.
|
||||
*
|
||||
* @param isMirror if true, image is a mirror-image of original
|
||||
* @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code
|
||||
* @throws NotFoundException if no Aztec Code can be found
|
||||
*/
|
||||
pub fn detect(
|
||||
&self,
|
||||
is_mirror: Option<bool>,
|
||||
) -> Result<AztecDetectorResult, NotFoundException> {
|
||||
// 1. Get the center of the aztec matrix
|
||||
let p_center: Point = self.get_matrix_center();
|
||||
// 2. Get the center points of the four diagonal points just outside the bull's eye
|
||||
// [topRight, bottomRight, bottomLeft, topLeft]
|
||||
let bulls_eye_corners: Vec<ResultPoint> = self.get_bulls_eye_corners(&p_center);
|
||||
if is_mirror.unwrap_or(false) {
|
||||
let temp: ResultPoint = bulls_eye_corners[0];
|
||||
bulls_eye_corners[0] = bulls_eye_corners[2];
|
||||
bulls_eye_corners[2] = temp;
|
||||
}
|
||||
// 3. Get the size of the matrix and other parameters from the bull's eye
|
||||
self.extract_parameters(&bulls_eye_corners);
|
||||
// 4. Sample the grid
|
||||
let bits: BitMatrix = self.sample_grid(
|
||||
&self.image,
|
||||
bulls_eye_corners[self.shift % 4],
|
||||
bulls_eye_corners[(self.shift + 1) % 4],
|
||||
bulls_eye_corners[(self.shift + 2) % 4],
|
||||
bulls_eye_corners[(self.shift + 3) % 4],
|
||||
);
|
||||
// 5. Get the corners of the matrix.
|
||||
let corners: Vec<ResultPoint> = self.get_matrix_corner_points(&bulls_eye_corners);
|
||||
return Ok(AztecDetectorResult::new(
|
||||
&bits,
|
||||
&corners,
|
||||
self.compact,
|
||||
self.nb_data_blocks,
|
||||
self.nb_layers,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the number of data layers and data blocks from the layer around the bull's eye.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @throws NotFoundException in case of too many errors or invalid parameters
|
||||
*/
|
||||
fn extract_parameters(
|
||||
&self,
|
||||
bulls_eye_corners: &Vec<ResultPoint>,
|
||||
) -> Result<(), NotFoundException> {
|
||||
if !self.is_valid(bulls_eye_corners[0])
|
||||
|| !self.is_valid(bulls_eye_corners[1])
|
||||
|| !self.is_valid(bulls_eye_corners[2])
|
||||
|| !self.is_valid(bulls_eye_corners[3])
|
||||
{
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let length: i32 = 2 * self.nb_center_layers;
|
||||
// Get the bits around the bull's eye
|
||||
let sides: vec![Vec<i32>; 4] = vec![
|
||||
// Right side
|
||||
self.sample_line(&bulls_eye_corners[0], &bulls_eye_corners[1], length), // Bottom
|
||||
self.sample_line(&bulls_eye_corners[1], &bulls_eye_corners[2], length), // Left side
|
||||
self.sample_line(&bulls_eye_corners[2], &bulls_eye_corners[3], length), // Top
|
||||
self.sample_line(&bulls_eye_corners[3], &bulls_eye_corners[0], length),
|
||||
];
|
||||
// bullsEyeCorners[shift] is the corner of the bulls'eye that has three
|
||||
// orientation marks.
|
||||
// sides[shift] is the row/column that goes from the corner with three
|
||||
// orientation marks to the corner with two.
|
||||
self.shift = ::get_rotation(&sides, length);
|
||||
// Flatten the parameter bits into a single 28- or 40-bit long
|
||||
let parameter_data: i64 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < 4 {
|
||||
{
|
||||
let side: i32 = sides[(self.shift + i) % 4];
|
||||
if self.compact {
|
||||
// Each side of the form ..XXXXXXX. where Xs are parameter data
|
||||
parameter_data <<= 7;
|
||||
parameter_data += (side >> 1) & 0x7F;
|
||||
} else {
|
||||
// Each side of the form ..XXXXX.XXXXX. where Xs are parameter data
|
||||
parameter_data <<= 10;
|
||||
parameter_data += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Corrects parameter data using RS. Returns just the data portion
|
||||
// without the error correction.
|
||||
let corrected_data: i32 = ::get_corrected_parameter_data(parameter_data, self.compact);
|
||||
if self.compact {
|
||||
// 8 bits: 2 bits layers and 6 bits data blocks
|
||||
self.nb_layers = (corrected_data >> 6) + 1;
|
||||
self.nb_data_blocks = (corrected_data & 0x3F) + 1;
|
||||
} else {
|
||||
// 16 bits: 5 bits layers and 11 bits data blocks
|
||||
self.nb_layers = (corrected_data >> 11) + 1;
|
||||
self.nb_data_blocks = (corrected_data & 0x7FF) + 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_rotation(sides: &Vec<i32>, length: i32) -> Result<i32, NotFoundException> {
|
||||
// In a normal pattern, we expect to See
|
||||
// ** .* D A
|
||||
// * *
|
||||
//
|
||||
// . *
|
||||
// .. .. C B
|
||||
//
|
||||
// Grab the 3 bits from each of the sides the form the locator pattern and concatenate
|
||||
// into a 12-bit integer. Start with the bit at A
|
||||
let corner_bits: i32 = 0;
|
||||
for side in sides {
|
||||
// XX......X where X's are orientation marks
|
||||
let t: i32 = ((side >> (length - 2)) << 1) + (side & 1);
|
||||
corner_bits = (corner_bits << 3) + t;
|
||||
}
|
||||
// Mov the bottom bit to the top, so that the three bits of the locator pattern at A are
|
||||
// together. cornerBits is now:
|
||||
// 3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D
|
||||
corner_bits = ((corner_bits & 1) << 11) + (corner_bits >> 1);
|
||||
// can easily tolerate two errors.
|
||||
{
|
||||
let mut shift: i32 = 0;
|
||||
while shift < 4 {
|
||||
{
|
||||
if Integer::bit_count(corner_bits ^ EXPECTED_CORNER_BITS[shift]) <= 2 {
|
||||
return Ok(shift);
|
||||
}
|
||||
}
|
||||
shift += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects the parameter bits using Reed-Solomon algorithm.
|
||||
*
|
||||
* @param parameterData parameter bits
|
||||
* @param compact true if this is a compact Aztec code
|
||||
* @throws NotFoundException if the array contains too many errors
|
||||
*/
|
||||
fn get_corrected_parameter_data(
|
||||
parameter_data: i64,
|
||||
compact: bool,
|
||||
) -> Result<i32, Rc<Exception>> {
|
||||
let num_codewords: i32;
|
||||
let num_data_codewords: i32;
|
||||
if compact {
|
||||
num_codewords = 7;
|
||||
num_data_codewords = 2;
|
||||
} else {
|
||||
num_codewords = 10;
|
||||
num_data_codewords = 4;
|
||||
}
|
||||
let num_e_c_codewords: i32 = num_codewords - num_data_codewords;
|
||||
let parameter_words: [i32; num_codewords] = [0; num_codewords];
|
||||
{
|
||||
let mut i: i32 = num_codewords - 1;
|
||||
while i >= 0 {
|
||||
{
|
||||
parameter_words[i] = parameter_data as i32 & 0xF;
|
||||
parameter_data >>= 4;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let tryResult1 = 0;
|
||||
/*'try1: loop {
|
||||
{*/
|
||||
let rs_decoder: ReedSolomonDecoder = ReedSolomonDecoder::new(GenericGF::AZTEC_PARAM);
|
||||
rs_decoder.decode(¶meter_words, num_e_c_codewords);
|
||||
/*}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( ignored: &ReedSolomonException) {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
} 0 => break
|
||||
}
|
||||
*/
|
||||
|
||||
// Toss the error correction. Just return the data as an integer
|
||||
let mut result: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_data_codewords {
|
||||
{
|
||||
result = (result << 4) + parameter_words[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the corners of a bull-eye centered on the passed point.
|
||||
* This returns the centers of the diagonal points just outside the bull's eye
|
||||
* Returns [topRight, bottomRight, bottomLeft, topLeft]
|
||||
*
|
||||
* @param pCenter Center point
|
||||
* @return The corners of the bull-eye
|
||||
* @throws NotFoundException If no valid bull-eye can be found
|
||||
*/
|
||||
fn get_bulls_eye_corners(&self, p_center: &Point) -> Result<Vec<ResultPoint>, Rc<Exception>> {
|
||||
let mut pina: Point = p_center;
|
||||
let mut pinb: Point = p_center;
|
||||
let mut pinc: Point = p_center;
|
||||
let mut pind: Point = p_center;
|
||||
let mut color: bool = true;
|
||||
{
|
||||
self.nb_center_layers = 1;
|
||||
while self.nb_center_layers < 9 {
|
||||
{
|
||||
let pouta: Point = self.get_first_different(&pina, color, 1, -1);
|
||||
let poutb: Point = self.get_first_different(&pinb, color, 1, 1);
|
||||
let poutc: Point = self.get_first_different(&pinc, color, -1, 1);
|
||||
let poutd: Point = self.get_first_different(&pind, color, -1, -1);
|
||||
if self.nb_center_layers > 2 {
|
||||
let q: f32 = ::distance(poutd, pouta) * self.nb_center_layers
|
||||
/ (::distance(pind, pina) * (self.nb_center_layers + 2));
|
||||
if q < 0.75
|
||||
|| q > 1.25
|
||||
|| !self.is_white_or_black_rectangle(&pouta, &poutb, &poutc, &poutd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
pina = pouta;
|
||||
pinb = poutb;
|
||||
pinc = poutc;
|
||||
pind = poutd;
|
||||
color = !color;
|
||||
}
|
||||
self.nb_center_layers += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if self.nb_center_layers != 5 && self.nb_center_layers != 7 {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
self.compact = self.nb_center_layers == 5;
|
||||
// Expand the square by .5 pixel in each direction so that we're on the border
|
||||
// between the white square and the black square
|
||||
let pinax: ResultPoint = ResultPoint::new(pina.get_x() + 0.5f32, pina.get_y() - 0.5f32);
|
||||
let pinbx: ResultPoint = ResultPoint::new(pinb.get_x() + 0.5f32, pinb.get_y() + 0.5f32);
|
||||
let pincx: ResultPoint = ResultPoint::new(pinc.get_x() - 0.5f32, pinc.get_y() + 0.5f32);
|
||||
let pindx: ResultPoint = ResultPoint::new(pind.get_x() - 0.5f32, pind.get_y() - 0.5f32);
|
||||
// just outside the bull's eye.
|
||||
return Ok(::expand_square(
|
||||
vec![pinax, pinbx, pincx, pindx],
|
||||
2 * self.nb_center_layers - 3,
|
||||
2 * self.nb_center_layers,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a candidate center point of an Aztec code from an image
|
||||
*
|
||||
* @return the center point
|
||||
*/
|
||||
fn get_matrix_center(&self) -> Point {
|
||||
let point_a: ResultPoint;
|
||||
let point_b: ResultPoint;
|
||||
let point_c: ResultPoint;
|
||||
let point_d: ResultPoint;
|
||||
//Get a white rectangle that can be the border of the matrix in center bull's eye or
|
||||
let tryResult1 = 0;
|
||||
|
||||
let corner_points_detector = WhiteRectangleDetector::new(&self.image, None, None, None);
|
||||
if corner_points_detector.is_ok() {
|
||||
let corner_points: Vec<ResultPoint> = corner_points_detector.detect();
|
||||
|
||||
point_a = corner_points[0];
|
||||
point_b = corner_points[1];
|
||||
point_c = corner_points[2];
|
||||
point_d = corner_points[3];
|
||||
} else {
|
||||
let cx: i32 = self.image.get_width() / 2;
|
||||
let cy: i32 = self.image.get_height() / 2;
|
||||
point_a = self
|
||||
.get_first_different(&Point::new(cx + 7, cy - 7), false, 1, -1)
|
||||
.to_result_point();
|
||||
point_b = self
|
||||
.get_first_different(&Point::new(cx + 7, cy + 7), false, 1, 1)
|
||||
.to_result_point();
|
||||
point_c = self
|
||||
.get_first_different(&Point::new(cx - 7, cy + 7), false, -1, 1)
|
||||
.to_result_point();
|
||||
point_d = self
|
||||
.get_first_different(&Point::new(cx - 7, cy - 7), false, -1, -1)
|
||||
.to_result_point();
|
||||
}
|
||||
|
||||
//Compute the center of the rectangle
|
||||
let mut cx: i32 = MathUtils::round(
|
||||
(point_a.get_x() + point_d.get_x() + point_b.get_x() + point_c.get_x()) / 4.0f32,
|
||||
);
|
||||
let mut cy: i32 = MathUtils::round(
|
||||
(point_a.get_y() + point_d.get_y() + point_b.get_y() + point_c.get_y()) / 4.0f32,
|
||||
);
|
||||
// in order to compute a more accurate center.
|
||||
let tryResult1 = 0;
|
||||
|
||||
let corner_points_wrd =
|
||||
WhiteRectangleDetector::new(&self.image, Some(15), Some(cx), Some(cy));
|
||||
if corner_points_wrd.is_ok() {
|
||||
let corner_points: Vec<ResultPoint> = corner_points_wrd.detect();
|
||||
point_a = corner_points[0];
|
||||
point_b = corner_points[1];
|
||||
point_c = corner_points[2];
|
||||
point_d = corner_points[3];
|
||||
} else {
|
||||
point_a = self
|
||||
.get_first_different(&Point::new(cx + 7, cy - 7), false, 1, -1)
|
||||
.to_result_point();
|
||||
point_b = self
|
||||
.get_first_different(&Point::new(cx + 7, cy + 7), false, 1, 1)
|
||||
.to_result_point();
|
||||
point_c = self
|
||||
.get_first_different(&Point::new(cx - 7, cy + 7), false, -1, 1)
|
||||
.to_result_point();
|
||||
point_d = self
|
||||
.get_first_different(&Point::new(cx - 7, cy - 7), false, -1, -1)
|
||||
.to_result_point();
|
||||
}
|
||||
|
||||
// Recompute the center of the rectangle
|
||||
cx = MathUtils::round(
|
||||
(point_a.get_x() + point_d.get_x() + point_b.get_x() + point_c.get_x()) / 4.0f32,
|
||||
);
|
||||
cy = MathUtils::round(
|
||||
(point_a.get_y() + point_d.get_y() + point_b.get_y() + point_c.get_y()) / 4.0f32,
|
||||
);
|
||||
return Point::new(cx, cy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Aztec code corners from the bull's eye corners and the parameters.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @return the array of aztec code corners
|
||||
*/
|
||||
fn get_matrix_corner_points(&self, bulls_eye_corners: &Vec<ResultPoint>) -> Vec<ResultPoint> {
|
||||
return ::expand_square(
|
||||
bulls_eye_corners,
|
||||
2 * self.nb_center_layers,
|
||||
&self.get_dimension(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BitMatrix by sampling the provided image.
|
||||
* topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
|
||||
* diagonal just outside the bull's eye.
|
||||
*/
|
||||
fn sample_grid(
|
||||
&self,
|
||||
image: &BitMatrix,
|
||||
top_left: &ResultPoint,
|
||||
top_right: &ResultPoint,
|
||||
bottom_right: &ResultPoint,
|
||||
bottom_left: &ResultPoint,
|
||||
) -> Result<BitMatrix, Rc<Exception>> {
|
||||
let sampler: GridSampler = GridSampler::get_instance();
|
||||
let dimension: i32 = self.get_dimension();
|
||||
let low: f32 = dimension / 2.0f32 - self.nb_center_layers;
|
||||
let high: f32 = dimension / 2.0f32 + self.nb_center_layers;
|
||||
return Ok(sampler.sample_grid(
|
||||
image,
|
||||
dimension,
|
||||
dimension, // topleft
|
||||
low, // topleft
|
||||
low, // topright
|
||||
high, // topright
|
||||
low, // bottomright
|
||||
high, // bottomright
|
||||
high, // bottomleft
|
||||
low, // bottomleft
|
||||
high,
|
||||
&top_left.get_x(),
|
||||
&top_left.get_y(),
|
||||
&top_right.get_x(),
|
||||
&top_right.get_y(),
|
||||
&bottom_right.get_x(),
|
||||
&bottom_right.get_y(),
|
||||
&bottom_left.get_x(),
|
||||
&bottom_left.get_y(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples a line.
|
||||
*
|
||||
* @param p1 start point (inclusive)
|
||||
* @param p2 end point (exclusive)
|
||||
* @param size number of bits
|
||||
* @return the array of bits as an int (first bit is high-order bit of result)
|
||||
*/
|
||||
fn sample_line(&self, p1: &ResultPoint, p2: &ResultPoint, size: i32) -> i32 {
|
||||
let mut result: i32 = 0;
|
||||
let d: f32 = ::distance(p1, p2);
|
||||
let module_size: f32 = d / size;
|
||||
let px: f32 = p1.get_x();
|
||||
let py: f32 = p1.get_y();
|
||||
let dx: f32 = module_size * (p2.get_x() - p1.get_x()) / d;
|
||||
let dy: f32 = module_size * (p2.get_y() - p1.get_y()) / d;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
if self.image.get(
|
||||
&MathUtils::round(px + i * dx),
|
||||
&MathUtils::round(py + i * dy),
|
||||
) {
|
||||
result |= 1 << (size - i - 1);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the border of the rectangle passed in parameter is compound of white points only
|
||||
* or black points only
|
||||
*/
|
||||
fn is_white_or_black_rectangle(&self, p1: &Point, p2: &Point, p3: &Point, p4: &Point) -> bool {
|
||||
let corr: i32 = 3;
|
||||
p1 = &Point::new(
|
||||
&Math::max(0, p1.get_x() - corr),
|
||||
&Math::min(self.image.get_height() - 1, p1.get_y() + corr),
|
||||
);
|
||||
p2 = &Point::new(
|
||||
&Math::max(0, p2.get_x() - corr),
|
||||
&Math::max(0, p2.get_y() - corr),
|
||||
);
|
||||
p3 = &Point::new(
|
||||
&Math::min(self.image.get_width() - 1, p3.get_x() + corr),
|
||||
&Math::max(
|
||||
0,
|
||||
&Math::min(self.image.get_height() - 1, p3.get_y() - corr),
|
||||
),
|
||||
);
|
||||
p4 = &Point::new(
|
||||
&Math::min(self.image.get_width() - 1, p4.get_x() + corr),
|
||||
&Math::min(self.image.get_height() - 1, p4.get_y() + corr),
|
||||
);
|
||||
let c_init: i32 = self.get_color(p4, p1);
|
||||
if c_init == 0 {
|
||||
return false;
|
||||
}
|
||||
let mut c: i32 = self.get_color(p1, p2);
|
||||
if c != c_init {
|
||||
return false;
|
||||
}
|
||||
c = self.get_color(p2, p3);
|
||||
if c != c_init {
|
||||
return false;
|
||||
}
|
||||
c = self.get_color(p3, p4);
|
||||
return c == c_init;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of a segment
|
||||
*
|
||||
* @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else
|
||||
*/
|
||||
fn get_color(&self, p1: &Point, p2: &Point) -> i32 {
|
||||
let d: f32 = ::distance(p1, p2);
|
||||
if d == 0.0f32 {
|
||||
return 0;
|
||||
}
|
||||
let dx: f32 = (p2.get_x() - p1.get_x()) / d;
|
||||
let dy: f32 = (p2.get_y() - p1.get_y()) / d;
|
||||
let mut error: i32 = 0;
|
||||
let mut px: f32 = p1.get_x();
|
||||
let mut py: f32 = p1.get_y();
|
||||
let color_model: bool = self.image.get(&p1.get_x(), &p1.get_y());
|
||||
let i_max: i32 = Math::floor(d) as i32;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < i_max {
|
||||
{
|
||||
if self.image.get(&MathUtils::round(px), &MathUtils::round(py)) != color_model {
|
||||
error += 1;
|
||||
}
|
||||
px += dx;
|
||||
py += dy;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let err_ratio: f32 = error / d;
|
||||
if err_ratio > 0.1f32 && err_ratio < 0.9f32 {
|
||||
return 0;
|
||||
}
|
||||
return if (err_ratio <= 0.1f32) == color_model {
|
||||
1
|
||||
} else {
|
||||
-1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the coordinate of the first point with a different color in the given direction
|
||||
*/
|
||||
fn get_first_different(&self, init: &Point, color: bool, dx: i32, dy: i32) -> Point {
|
||||
let mut x: i32 = init.get_x() + dx;
|
||||
let mut y: i32 = init.get_y() + dy;
|
||||
while self.is_valid(x, y) && self.image.get(x, y) == color {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
while self.is_valid(x, y) && self.image.get(x, y) == color {
|
||||
x += dx;
|
||||
}
|
||||
x -= dx;
|
||||
while self.is_valid(x, y) && self.image.get(x, y) == color {
|
||||
y += dy;
|
||||
}
|
||||
y -= dy;
|
||||
return Point::new(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the square represented by the corner points by pushing out equally in all directions
|
||||
*
|
||||
* @param cornerPoints the corners of the square, which has the bull's eye at its center
|
||||
* @param oldSide the original length of the side of the square in the target bit matrix
|
||||
* @param newSide the new length of the size of the square in the target bit matrix
|
||||
* @return the corners of the expanded square
|
||||
*/
|
||||
fn expand_square(
|
||||
corner_points: &Vec<ResultPoint>,
|
||||
old_side: i32,
|
||||
new_side: i32,
|
||||
) -> Vec<ResultPoint> {
|
||||
let ratio: f32 = new_side / (2.0f32 * old_side);
|
||||
let mut dx: f32 = corner_points[0].get_x() - corner_points[2].get_x();
|
||||
let mut dy: f32 = corner_points[0].get_y() - corner_points[2].get_y();
|
||||
let mut centerx: f32 = (corner_points[0].get_x() + corner_points[2].get_x()) / 2.0f32;
|
||||
let mut centery: f32 = (corner_points[0].get_y() + corner_points[2].get_y()) / 2.0f32;
|
||||
let result0: ResultPoint = ResultPoint::new(centerx + ratio * dx, centery + ratio * dy);
|
||||
let result2: ResultPoint = ResultPoint::new(centerx - ratio * dx, centery - ratio * dy);
|
||||
dx = corner_points[1].get_x() - corner_points[3].get_x();
|
||||
dy = corner_points[1].get_y() - corner_points[3].get_y();
|
||||
centerx = (corner_points[1].get_x() + corner_points[3].get_x()) / 2.0f32;
|
||||
centery = (corner_points[1].get_y() + corner_points[3].get_y()) / 2.0f32;
|
||||
let result1: ResultPoint = ResultPoint::new(centerx + ratio * dx, centery + ratio * dy);
|
||||
let result3: ResultPoint = ResultPoint::new(centerx - ratio * dx, centery - ratio * dy);
|
||||
return vec![result0, result1, result2, result3];
|
||||
}
|
||||
|
||||
fn is_valid_coords(&self, x: i32, y: i32) -> bool {
|
||||
return x >= 0 && x < self.image.get_width() && y >= 0 && y < self.image.get_height();
|
||||
}
|
||||
|
||||
fn is_valid_rp(&self, point: &ResultPoint) -> bool {
|
||||
let x: i32 = MathUtils::round(&point.get_x());
|
||||
let y: i32 = MathUtils::round(&point.get_y());
|
||||
return self.is_valid(x, y);
|
||||
}
|
||||
|
||||
fn distance(a: &Point, b: &Point) -> f32 {
|
||||
return MathUtils::distance(&a.get_x(), &a.get_y(), &b.get_x(), &b.get_y());
|
||||
}
|
||||
|
||||
fn distance(a: &ResultPoint, b: &ResultPoint) -> f32 {
|
||||
return MathUtils::distance(&a.get_x(), &a.get_y(), &b.get_x(), &b.get_y());
|
||||
}
|
||||
|
||||
fn get_dimension(&self) -> i32 {
|
||||
if self.compact {
|
||||
return 4 * self.nb_layers + 11;
|
||||
}
|
||||
return 4 * self.nb_layers + 2 * ((2 * self.nb_layers + 6) / 15) + 15;
|
||||
}
|
||||
}
|
||||
|
||||
struct Point {
|
||||
x: i32,
|
||||
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
fn to_result_point(&self) -> ResultPoint {
|
||||
return ResultPoint::new(self.x, self.y);
|
||||
}
|
||||
|
||||
fn new(x: i32, y: i32) -> Self {
|
||||
Self { x: x, y: y }
|
||||
}
|
||||
|
||||
fn get_x(&self) -> i32 {
|
||||
return self.x;
|
||||
}
|
||||
|
||||
fn get_y(&self) -> i32 {
|
||||
return self.y;
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
return format!("<{} {}>", self.x, self.y);
|
||||
}
|
||||
}
|
||||
1387
src/aztec/encoder.rs
1387
src/aztec/encoder.rs
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
pub mod result;
|
||||
3525
src/client/result.rs
3525
src/client/result.rs
File diff suppressed because it is too large
Load Diff
3750
src/common.rs
3750
src/common.rs
File diff suppressed because it is too large
Load Diff
@@ -1,708 +0,0 @@
|
||||
use crate::common::{BitMatrix, BitMatrix};
|
||||
use crate::{NotFoundException, ResultPoint};
|
||||
|
||||
// MathUtils.java
|
||||
/**
|
||||
* General math-related and numeric utility functions.
|
||||
*/
|
||||
pub struct MathUtils {}
|
||||
|
||||
impl MathUtils {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
|
||||
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
|
||||
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
|
||||
*
|
||||
* @param d real value to round
|
||||
* @return nearest {@code int}
|
||||
*/
|
||||
pub fn round(d: f32) -> i32 {
|
||||
return (d + (if d < 0.0f32 { -0.5f32 } else { 0.5f32 })) as i32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aX point A x coordinate
|
||||
* @param aY point A y coordinate
|
||||
* @param bX point B x coordinate
|
||||
* @param bY point B y coordinate
|
||||
* @return Euclidean distance between points A and B
|
||||
*/
|
||||
pub fn distance(a_x: f32, a_y: f32, b_x: f32, b_y: f32) -> f32 {
|
||||
let x_diff: f64 = a_x - b_x;
|
||||
let y_diff: f64 = a_y - b_y;
|
||||
return Math::sqrt(x_diff * x_diff + y_diff * y_diff) as f32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param aX point A x coordinate
|
||||
* @param aY point A y coordinate
|
||||
* @param bX point B x coordinate
|
||||
* @param bY point B y coordinate
|
||||
* @return Euclidean distance between points A and B
|
||||
*/
|
||||
pub fn distance(a_x: i32, a_y: i32, b_x: i32, b_y: i32) -> f32 {
|
||||
let x_diff: f64 = a_x - b_x;
|
||||
let y_diff: f64 = a_y - b_y;
|
||||
return Math::sqrt(x_diff * x_diff + y_diff * y_diff) as f32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array values to sum
|
||||
* @return sum of values in array
|
||||
*/
|
||||
pub fn sum(array: &Vec<i32>) -> i32 {
|
||||
let mut count: i32 = 0;
|
||||
for a in array {
|
||||
count += a;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// MonochromeRectangleDetector.java
|
||||
/**
|
||||
* <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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||
* white, in an image.</p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] 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<ResultPoint>, Rc<Exception>> {
|
||||
let height: i32 = self.image.get_height();
|
||||
let width: i32 = self.image.get_width();
|
||||
let half_height: i32 = height / 2;
|
||||
let half_width: i32 = width / 2;
|
||||
let delta_y: i32 = Math::max(1, height / (MAX_MODULES * 8));
|
||||
let delta_x: i32 = Math::max(1, width / (MAX_MODULES * 8));
|
||||
let mut top: i32 = 0;
|
||||
let mut bottom: i32 = height;
|
||||
let mut left: i32 = 0;
|
||||
let mut right: i32 = width;
|
||||
let point_a: ResultPoint = self.find_corner_from_center(
|
||||
half_width,
|
||||
0,
|
||||
left,
|
||||
right,
|
||||
half_height,
|
||||
-delta_y,
|
||||
top,
|
||||
bottom,
|
||||
half_width / 2,
|
||||
);
|
||||
top = point_a.get_y() as i32 - 1;
|
||||
let point_b: ResultPoint = self.find_corner_from_center(
|
||||
half_width,
|
||||
-delta_x,
|
||||
left,
|
||||
right,
|
||||
half_height,
|
||||
0,
|
||||
top,
|
||||
bottom,
|
||||
half_height / 2,
|
||||
);
|
||||
left = point_b.get_x() as i32 - 1;
|
||||
let point_c: ResultPoint = self.find_corner_from_center(
|
||||
half_width,
|
||||
delta_x,
|
||||
left,
|
||||
right,
|
||||
half_height,
|
||||
0,
|
||||
top,
|
||||
bottom,
|
||||
half_height / 2,
|
||||
);
|
||||
right = point_c.get_x() as i32 + 1;
|
||||
let point_d: ResultPoint = self.find_corner_from_center(
|
||||
half_width,
|
||||
0,
|
||||
left,
|
||||
right,
|
||||
half_height,
|
||||
delta_y,
|
||||
top,
|
||||
bottom,
|
||||
half_width / 2,
|
||||
);
|
||||
bottom = point_d.get_y() as i32 + 1;
|
||||
// Go try to find point A again with better information -- might have been off at first.
|
||||
point_a = self.find_corner_from_center(
|
||||
half_width,
|
||||
0,
|
||||
left,
|
||||
right,
|
||||
half_height,
|
||||
-delta_y,
|
||||
top,
|
||||
bottom,
|
||||
half_width / 4,
|
||||
);
|
||||
return Ok(vec![point_a, point_b, point_c, point_d]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ResultPoint} encapsulating the corner that was found
|
||||
* @throws NotFoundException if such a point cannot be found
|
||||
*/
|
||||
fn find_corner_from_center(
|
||||
&self,
|
||||
center_x: i32,
|
||||
delta_x: i32,
|
||||
left: i32,
|
||||
right: i32,
|
||||
center_y: i32,
|
||||
delta_y: i32,
|
||||
top: i32,
|
||||
bottom: i32,
|
||||
max_white_run: i32,
|
||||
) -> Result<ResultPoint, NotFoundException> {
|
||||
let last_range: Vec<i32> = null;
|
||||
{
|
||||
let mut y: i32 = center_y;
|
||||
let mut x: i32 = center_x;
|
||||
while y < bottom && y >= top && x < right && x >= left {
|
||||
{
|
||||
let mut range: Vec<i32>;
|
||||
if delta_x == 0 {
|
||||
// horizontal slices, up and down
|
||||
range = self.black_white_range(y, max_white_run, left, right, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
range = self.black_white_range(x, max_white_run, top, bottom, false);
|
||||
}
|
||||
if range == null {
|
||||
if last_range == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
// lastRange was found
|
||||
if delta_x == 0 {
|
||||
let last_y: i32 = y - delta_y;
|
||||
if last_range[0] < center_x {
|
||||
if last_range[1] > center_x {
|
||||
// straddle, choose one or the other based on direction
|
||||
return Ok(ResultPoint::new(
|
||||
last_range[if delta_y > 0 { 0 } else { 1 }],
|
||||
last_y,
|
||||
));
|
||||
}
|
||||
return Ok(ResultPoint::new(last_range[0], last_y));
|
||||
} else {
|
||||
return Ok(ResultPoint::new(last_range[1], last_y));
|
||||
}
|
||||
} else {
|
||||
let last_x: i32 = x - delta_x;
|
||||
if last_range[0] < center_y {
|
||||
if last_range[1] > center_y {
|
||||
return Ok(ResultPoint::new(
|
||||
last_x,
|
||||
last_range[if delta_x < 0 { 0 } else { 1 }],
|
||||
));
|
||||
}
|
||||
return Ok(ResultPoint::new(last_x, last_range[0]));
|
||||
} else {
|
||||
return Ok(ResultPoint::new(last_x, last_range[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
last_range = range;
|
||||
}
|
||||
y += delta_y;
|
||||
x += delta_x;
|
||||
}
|
||||
}
|
||||
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 black_white_range(
|
||||
&self,
|
||||
fixed_dimension: i32,
|
||||
max_white_run: i32,
|
||||
min_dim: i32,
|
||||
max_dim: i32,
|
||||
horizontal: bool,
|
||||
) -> Option<Vec<i32>> {
|
||||
let center: i32 = (min_dim + max_dim) / 2;
|
||||
// Scan left/up first
|
||||
let mut start: i32 = center;
|
||||
while start >= min_dim {
|
||||
if if horizontal {
|
||||
self.image.get(start, fixed_dimension)
|
||||
} else {
|
||||
self.image.get(fixed_dimension, start)
|
||||
} {
|
||||
start -= 1;
|
||||
} else {
|
||||
let white_run_start: i32 = start;
|
||||
loop {
|
||||
{
|
||||
start -= 1;
|
||||
}
|
||||
if !(start >= min_dim
|
||||
&& !(if horizontal {
|
||||
self.image.get(start, fixed_dimension)
|
||||
} else {
|
||||
self.image.get(fixed_dimension, start)
|
||||
}))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
let white_run_size: i32 = white_run_start - start;
|
||||
if start < min_dim || white_run_size > max_white_run {
|
||||
start = white_run_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
start += 1;
|
||||
// Then try right/down
|
||||
let mut end: i32 = center;
|
||||
while end < max_dim {
|
||||
if if horizontal {
|
||||
self.image.get(end, fixed_dimension)
|
||||
} else {
|
||||
self.image.get(fixed_dimension, end)
|
||||
} {
|
||||
end += 1;
|
||||
} else {
|
||||
let white_run_start: i32 = end;
|
||||
loop {
|
||||
{
|
||||
end += 1;
|
||||
}
|
||||
if !(end < max_dim
|
||||
&& !(if horizontal {
|
||||
self.image.get(end, fixed_dimension)
|
||||
} else {
|
||||
self.image.get(fixed_dimension, end)
|
||||
}))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
let white_run_size: i32 = end - white_run_start;
|
||||
if end >= max_dim || white_run_size > max_white_run {
|
||||
end = white_run_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
end -= 1;
|
||||
return if end > start {
|
||||
Some(vec![start, end])
|
||||
} else {
|
||||
null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// WhiteRectangleDetector.java
|
||||
/**
|
||||
* <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,
|
||||
|
||||
left_init: i32,
|
||||
|
||||
right_init: i32,
|
||||
|
||||
down_init: i32,
|
||||
|
||||
up_init: i32,
|
||||
}
|
||||
|
||||
impl WhiteRectangleDetector {
|
||||
/**
|
||||
* @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,
|
||||
init_size: Option<i32>,
|
||||
x_in: Option<i32>,
|
||||
y_in: Option<i32>,
|
||||
) -> Result<Self, NotFoundException> {
|
||||
let mut new_wrd: Self;
|
||||
let x = x_in.unwrap_or(image.get_width() / 2);
|
||||
let y = y_in.unwrap_or(image.get_height() / 2);
|
||||
|
||||
new_wrd.image = image;
|
||||
new_wrd.height = image.get_height();
|
||||
new_wrd.width = image.get_width();
|
||||
let halfsize: i32 = init_size.unwrap_or(INIT_SIZE) / 2;
|
||||
new_wrd.left_init = x - halfsize;
|
||||
new_wrd.right_init = x + halfsize;
|
||||
new_wrd.up_init = y - halfsize;
|
||||
new_wrd.down_init = y + halfsize;
|
||||
|
||||
if up_init < 0 || left_init < 0 || down_init >= height || right_init >= width {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
|
||||
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 ResultPoint}[] 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<ResultPoint>, NotFoundException> {
|
||||
let mut left: i32 = self.left_init;
|
||||
let mut right: i32 = self.right_init;
|
||||
let mut up: i32 = self.up_init;
|
||||
let mut down: i32 = self.down_init;
|
||||
let size_exceeded: bool = false;
|
||||
let a_black_point_found_on_border: bool = true;
|
||||
let at_least_one_black_point_found_on_right: bool = false;
|
||||
let at_least_one_black_point_found_on_bottom: bool = false;
|
||||
let at_least_one_black_point_found_on_left: bool = false;
|
||||
let at_least_one_black_point_found_on_top: bool = false;
|
||||
while a_black_point_found_on_border {
|
||||
a_black_point_found_on_border = false;
|
||||
// .....
|
||||
// . |
|
||||
// .....
|
||||
let right_border_not_white: bool = true;
|
||||
while (right_border_not_white || !at_least_one_black_point_found_on_right)
|
||||
&& right < self.width
|
||||
{
|
||||
right_border_not_white = self.contains_black_point(up, down, right, false);
|
||||
if right_border_not_white {
|
||||
right += 1;
|
||||
a_black_point_found_on_border = true;
|
||||
at_least_one_black_point_found_on_right = true;
|
||||
} else if !at_least_one_black_point_found_on_right {
|
||||
right += 1;
|
||||
}
|
||||
}
|
||||
if right >= self.width {
|
||||
size_exceeded = true;
|
||||
break;
|
||||
}
|
||||
// .....
|
||||
// . .
|
||||
// .___.
|
||||
let bottom_border_not_white: bool = true;
|
||||
while (bottom_border_not_white || !at_least_one_black_point_found_on_bottom)
|
||||
&& down < self.height
|
||||
{
|
||||
bottom_border_not_white = self.contains_black_point(left, right, down, true);
|
||||
if bottom_border_not_white {
|
||||
down += 1;
|
||||
a_black_point_found_on_border = true;
|
||||
at_least_one_black_point_found_on_bottom = true;
|
||||
} else if !at_least_one_black_point_found_on_bottom {
|
||||
down += 1;
|
||||
}
|
||||
}
|
||||
if down >= self.height {
|
||||
size_exceeded = true;
|
||||
break;
|
||||
}
|
||||
// .....
|
||||
// | .
|
||||
// .....
|
||||
let left_border_not_white: bool = true;
|
||||
while (left_border_not_white || !at_least_one_black_point_found_on_left) && left >= 0 {
|
||||
left_border_not_white = self.contains_black_point(up, down, left, false);
|
||||
if left_border_not_white {
|
||||
left -= 1;
|
||||
a_black_point_found_on_border = true;
|
||||
at_least_one_black_point_found_on_left = true;
|
||||
} else if !at_least_one_black_point_found_on_left {
|
||||
left -= 1;
|
||||
}
|
||||
}
|
||||
if left < 0 {
|
||||
size_exceeded = true;
|
||||
break;
|
||||
}
|
||||
// .___.
|
||||
// . .
|
||||
// .....
|
||||
let top_border_not_white: bool = true;
|
||||
while (top_border_not_white || !at_least_one_black_point_found_on_top) && up >= 0 {
|
||||
top_border_not_white = self.contains_black_point(left, right, up, true);
|
||||
if top_border_not_white {
|
||||
up -= 1;
|
||||
a_black_point_found_on_border = true;
|
||||
at_least_one_black_point_found_on_top = true;
|
||||
} else if !at_least_one_black_point_found_on_top {
|
||||
up -= 1;
|
||||
}
|
||||
}
|
||||
if up < 0 {
|
||||
size_exceeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !size_exceeded {
|
||||
let max_size: i32 = right - left;
|
||||
let mut z: ResultPoint = null;
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while z == null && i < max_size {
|
||||
{
|
||||
z = self.get_black_point_on_segment(left, down - i, left + i, down);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if z == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let mut t: ResultPoint = null;
|
||||
//go down right
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while t == null && i < max_size {
|
||||
{
|
||||
t = self.get_black_point_on_segment(left, up + i, left + i, up);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if t == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let mut x: ResultPoint = null;
|
||||
//go down left
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while x == null && i < max_size {
|
||||
{
|
||||
x = self.get_black_point_on_segment(right, up + i, right - i, up);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if x == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let mut y: ResultPoint = null;
|
||||
//go up left
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while y == null && i < max_size {
|
||||
{
|
||||
y = self.get_black_point_on_segment(right, down - i, right - i, down);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if y == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
return Ok(self.center_edges(&y, &z, &x, &t));
|
||||
} else {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_black_point_on_segment(
|
||||
&self,
|
||||
a_x: f32,
|
||||
a_y: f32,
|
||||
b_x: f32,
|
||||
b_y: f32,
|
||||
) -> Option<ResultPoint> {
|
||||
let dist: i32 = MathUtils::round(&MathUtils::distance(a_x, a_y, b_x, b_y));
|
||||
let x_step: f32 = (b_x - a_x) / dist;
|
||||
let y_step: f32 = (b_y - a_y) / dist;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < dist {
|
||||
{
|
||||
let x: i32 = MathUtils::round(a_x + i * x_step);
|
||||
let y: i32 = MathUtils::round(a_y + i * y_step);
|
||||
if self.image.get(x, y) {
|
||||
return Some(ResultPoint::new(x, y));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ResultPoint}[] describing the corners of the rectangular
|
||||
* region. The first and last points are opposed on the diagonal, as
|
||||
* are the second and third. The first point will be the topmost
|
||||
* point and the last, the bottommost. The second point will be
|
||||
* leftmost and the third, the rightmost
|
||||
*/
|
||||
fn center_edges(
|
||||
&self,
|
||||
y: &ResultPoint,
|
||||
z: &ResultPoint,
|
||||
x: &ResultPoint,
|
||||
t: &ResultPoint,
|
||||
) -> Vec<ResultPoint> {
|
||||
//
|
||||
// t t
|
||||
// z x
|
||||
// x OR z
|
||||
// y y
|
||||
//
|
||||
let yi: f32 = y.get_x();
|
||||
let yj: f32 = y.get_y();
|
||||
let zi: f32 = z.get_x();
|
||||
let zj: f32 = z.get_y();
|
||||
let xi: f32 = x.get_x();
|
||||
let xj: f32 = x.get_y();
|
||||
let ti: f32 = t.get_x();
|
||||
let tj: f32 = t.get_y();
|
||||
if yi < self.width / 2.0f32 {
|
||||
return vec![
|
||||
ResultPoint::new(ti - CORR, tj + CORR),
|
||||
ResultPoint::new(zi + CORR, zj + CORR),
|
||||
ResultPoint::new(xi - CORR, xj - CORR),
|
||||
ResultPoint::new(yi + CORR, yj - CORR),
|
||||
];
|
||||
} else {
|
||||
return vec![
|
||||
ResultPoint::new(ti + CORR, tj + CORR),
|
||||
ResultPoint::new(zi + CORR, zj - CORR),
|
||||
ResultPoint::new(xi - CORR, xj + CORR),
|
||||
ResultPoint::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 contains_black_point(&self, a: i32, b: i32, fixed: i32, horizontal: bool) -> bool {
|
||||
if horizontal {
|
||||
{
|
||||
let mut x: i32 = a;
|
||||
while x <= b {
|
||||
{
|
||||
if self.image.get(x, fixed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
{
|
||||
let mut y: i32 = a;
|
||||
while y <= b {
|
||||
{
|
||||
if self.image.get(fixed, y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,849 +0,0 @@
|
||||
// GenericGFPoly.java
|
||||
/**
|
||||
* <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
|
||||
*/
|
||||
struct GenericGFPoly {
|
||||
field: GenericGF,
|
||||
|
||||
coefficients: Vec<i32>,
|
||||
}
|
||||
|
||||
impl GenericGFPoly {
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients coefficients as ints representing elements of GF(size), arranged
|
||||
* from most significant (highest-power term) coefficient to least significant
|
||||
* @throws IllegalArgumentException if argument is null or empty,
|
||||
* or if leading coefficient is 0 and this is not a
|
||||
* constant polynomial (that is, it is not the monomial "0")
|
||||
*/
|
||||
fn new(field: &GenericGF, coefficients: &Vec<i32>) -> Result<Self, IllegalArgumentException> {
|
||||
let mut new_poly: GenericGFPoly;
|
||||
if coefficients.len() == 0 {
|
||||
return Err(IllegalArgumentException::new());
|
||||
}
|
||||
new_poly.field = field;
|
||||
let coefficients_length: i32 = coefficients.len();
|
||||
if coefficients_length > 1 && coefficients[0] == 0 {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
let first_non_zero: i32 = 1;
|
||||
while first_non_zero < coefficients_length && coefficients[first_non_zero] == 0 {
|
||||
first_non_zero += 1;
|
||||
}
|
||||
if first_non_zero == coefficients_length {
|
||||
new_poly.coefficients = vec![0];
|
||||
} else {
|
||||
new_poly.coefficients = coefficients;
|
||||
//System::arraycopy(&coefficients, first_non_zero, let .coefficients, 0, let .coefficients.len());
|
||||
}
|
||||
} else {
|
||||
new_poly.coefficients = coefficients;
|
||||
}
|
||||
Ok(new_poly)
|
||||
}
|
||||
|
||||
fn get_coefficients(&self) -> Vec<i32> {
|
||||
return self.coefficients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
fn get_degree(&self) -> i32 {
|
||||
return self.coefficients.len() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
fn is_zero(&self) -> bool {
|
||||
return self.coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
fn get_coefficient(&self, degree: i32) -> i32 {
|
||||
return self.coefficients[self.coefficients.len() - 1 - degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
fn evaluate_at(&self, a: i32) -> i32 {
|
||||
if a == 0 {
|
||||
// Just return the x^0 coefficient
|
||||
return self.get_coefficient(0);
|
||||
}
|
||||
if a == 1 {
|
||||
// Just the sum of the coefficients
|
||||
let mut result: i32 = 0;
|
||||
for coefficient in self.coefficients {
|
||||
result = GenericGF::add_or_subtract(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let mut result: i32 = self.coefficients[0];
|
||||
let size: i32 = self.coefficients.len();
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < size {
|
||||
{
|
||||
result = GenericGF::add_or_subtract(
|
||||
&self.field.multiply(a, result),
|
||||
self.coefficients[i],
|
||||
);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn add_or_subtract(
|
||||
&self,
|
||||
other: &GenericGFPoly,
|
||||
) -> Result<GenericGFPoly, IllegalArgumentException> {
|
||||
if !self.field.equals(other.field) {
|
||||
return Err(IllegalArgumentException::new(
|
||||
"GenericGFPolys do not have same GenericGF field",
|
||||
));
|
||||
}
|
||||
if self.is_zero() {
|
||||
return other;
|
||||
}
|
||||
if other.is_zero() {
|
||||
return self;
|
||||
}
|
||||
let smaller_coefficients: Vec<i32> = self.coefficients;
|
||||
let larger_coefficients: Vec<i32> = other.coefficients;
|
||||
if smaller_coefficients.len() > larger_coefficients.len() {
|
||||
let temp: Vec<i32> = smaller_coefficients;
|
||||
smaller_coefficients = larger_coefficients;
|
||||
larger_coefficients = temp;
|
||||
}
|
||||
let sum_diff: [i32; larger_coefficients.len()] = [0; larger_coefficients.len()];
|
||||
let length_diff: i32 = larger_coefficients.len() - smaller_coefficients.len();
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
System::arraycopy(&larger_coefficients, 0, &sum_diff, 0, length_diff);
|
||||
{
|
||||
let mut i: i32 = length_diff;
|
||||
while i < larger_coefficients.len() {
|
||||
{
|
||||
sum_diff[i] = GenericGF::add_or_subtract(
|
||||
smaller_coefficients[i - length_diff],
|
||||
larger_coefficients[i],
|
||||
);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return GenericGFPoly::new(&self.field, &sum_diff);
|
||||
}
|
||||
|
||||
fn multiply(&self, other: &GenericGFPoly) -> Result<GenericGFPoly, IllegalArgumentException> {
|
||||
if !self.field.equals(other.field) {
|
||||
return Err(IllegalArgumentException::new(
|
||||
"GenericGFPolys do not have same GenericGF field",
|
||||
));
|
||||
}
|
||||
if self.is_zero() || other.is_zero() {
|
||||
return Ok(self.field.get_zero());
|
||||
}
|
||||
let a_coefficients: Vec<i32> = self.coefficients;
|
||||
let a_length: i32 = a_coefficients.len();
|
||||
let b_coefficients: Vec<i32> = other.coefficients;
|
||||
let b_length: i32 = b_coefficients.len();
|
||||
let mut product: [i32; a_length + b_length - 1] = [0; a_length + b_length - 1];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < a_length {
|
||||
{
|
||||
let a_coeff: i32 = a_coefficients[i];
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < b_length {
|
||||
{
|
||||
product[i + j] = GenericGF::add_or_subtract(
|
||||
product[i + j],
|
||||
&self.field.multiply(a_coeff, b_coefficients[j]),
|
||||
);
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return GenericGFPoly::new(&self.field, &product);
|
||||
}
|
||||
|
||||
fn multiply(&self, scalar: i32) -> GenericGFPoly {
|
||||
if scalar == 0 {
|
||||
return self.field.get_zero();
|
||||
}
|
||||
if scalar == 1 {
|
||||
return self;
|
||||
}
|
||||
let size: i32 = self.coefficients.len();
|
||||
let mut product: [i32; size] = [0; size];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
product[i] = self.field.multiply(self.coefficients[i], scalar);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return GenericGFPoly::new(&self.field, &product);
|
||||
}
|
||||
|
||||
fn multiply_by_monomial(
|
||||
&self,
|
||||
degree: i32,
|
||||
coefficient: i32,
|
||||
) -> Result<GenericGFPoly, IllegalArgumentException> {
|
||||
if degree < 0 {
|
||||
return Err(IllegalArgumentException::new());
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return Ok(self.field.get_zero());
|
||||
}
|
||||
let size: i32 = self.coefficients.len();
|
||||
let mut product: [i32; size + degree] = [0; size + degree];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
product[i] = self.field.multiply(self.coefficients[i], coefficient);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return GenericGFPoly::new(&self.field, &product);
|
||||
}
|
||||
|
||||
fn divide(
|
||||
&self,
|
||||
other: &GenericGFPoly,
|
||||
) -> Result<Vec<GenericGFPoly>, IllegalArgumentException> {
|
||||
if !self.field.equals(other.field) {
|
||||
return Err(IllegalArgumentException::new(
|
||||
"GenericGFPolys do not have same GenericGF field",
|
||||
));
|
||||
}
|
||||
if other.is_zero() {
|
||||
return Err(IllegalArgumentException::new("Divide by 0"));
|
||||
}
|
||||
let mut quotient: GenericGFPoly = self.field.get_zero();
|
||||
let mut remainder: GenericGFPoly = self;
|
||||
let denominator_leading_term: i32 = other.get_coefficient(&other.get_degree());
|
||||
let inverse_denominator_leading_term: i32 = self.field.inverse(denominator_leading_term);
|
||||
while remainder.get_degree() >= other.get_degree() && !remainder.is_zero() {
|
||||
let degree_difference: i32 = remainder.get_degree() - other.get_degree();
|
||||
let scale: i32 = self.field.multiply(
|
||||
&remainder.get_coefficient(&remainder.get_degree()),
|
||||
inverse_denominator_leading_term,
|
||||
);
|
||||
let term: GenericGFPoly = other.multiply_by_monomial(degree_difference, scale);
|
||||
let iteration_quotient: GenericGFPoly =
|
||||
self.field.build_monomial(degree_difference, scale);
|
||||
quotient = quotient.add_or_subtract(&iteration_quotient);
|
||||
remainder = remainder.add_or_subtract(&term);
|
||||
}
|
||||
return Ok(vec![quotient, remainder]);
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
if self.is_zero() {
|
||||
return "0".to_owned();
|
||||
}
|
||||
let result: StringBuilder = StringBuilder::new(8 * self.get_degree());
|
||||
{
|
||||
let mut degree: i32 = self.get_degree();
|
||||
while degree >= 0 {
|
||||
{
|
||||
let mut coefficient: i32 = self.get_coefficient(degree);
|
||||
if coefficient != 0 {
|
||||
if coefficient < 0 {
|
||||
if degree == self.get_degree() {
|
||||
result.append("-");
|
||||
} else {
|
||||
result.append(" - ");
|
||||
}
|
||||
coefficient = -coefficient;
|
||||
} else {
|
||||
if result.length() > 0 {
|
||||
result.append(" + ");
|
||||
}
|
||||
}
|
||||
if degree == 0 || coefficient != 1 {
|
||||
let alpha_power: i32 = self.field.log(coefficient);
|
||||
if alpha_power == 0 {
|
||||
result.append('1');
|
||||
} else if alpha_power == 1 {
|
||||
result.append('a');
|
||||
} else {
|
||||
result.append("a^");
|
||||
result.append(alpha_power);
|
||||
}
|
||||
}
|
||||
if degree != 0 {
|
||||
if degree == 1 {
|
||||
result.append('x');
|
||||
} else {
|
||||
result.append("x^");
|
||||
result.append(degree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
degree -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// GenericGF.java
|
||||
/**
|
||||
* <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
|
||||
*/
|
||||
|
||||
// x^12 + x^6 + x^5 + x^3 + 1
|
||||
const AZTEC_DATA_12: GenericGF = GenericGF::new(0x1069, 4096, 1);
|
||||
|
||||
// x^10 + x^3 + 1
|
||||
const AZTEC_DATA_10: GenericGF = GenericGF::new(0x409, 1024, 1);
|
||||
|
||||
// x^6 + x + 1
|
||||
const AZTEC_DATA_6: GenericGF = GenericGF::new(0x43, 64, 1);
|
||||
|
||||
// x^4 + x + 1
|
||||
const AZTEC_PARAM: GenericGF = GenericGF::new(0x13, 16, 1);
|
||||
|
||||
// x^8 + x^4 + x^3 + x^2 + 1
|
||||
const QR_CODE_FIELD_256: GenericGF = GenericGF::new(0x011D, 256, 0);
|
||||
|
||||
// x^8 + x^5 + x^3 + x^2 + 1
|
||||
const DATA_MATRIX_FIELD_256: GenericGF = GenericGF::new(0x012D, 256, 1);
|
||||
|
||||
const AZTEC_DATA_8: GenericGF = DATA_MATRIX_FIELD_256;
|
||||
|
||||
const MAXICODE_FIELD_64: GenericGF = AZTEC_DATA_6;
|
||||
|
||||
pub struct GenericGF {
|
||||
exp_table: Vec<i32>,
|
||||
|
||||
log_table: Vec<i32>,
|
||||
|
||||
zero: GenericGFPoly,
|
||||
|
||||
one: GenericGFPoly,
|
||||
|
||||
size: i32,
|
||||
|
||||
primitive: i32,
|
||||
|
||||
generator_base: i32,
|
||||
}
|
||||
|
||||
impl GenericGF {
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
pub fn new(primitive: i32, size: i32, b: i32) -> Self {
|
||||
let mut new_generic_gf: GenericGF;
|
||||
new_generic_gf.primitive = primitive;
|
||||
new_generic_gf.size = size;
|
||||
new_generic_gf.generatorBase = b;
|
||||
exp_table = [0; size];
|
||||
log_table = [0; size];
|
||||
let mut x: i32 = 1;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
exp_table[i] = x;
|
||||
// we're assuming the generator alpha is 2
|
||||
x *= 2;
|
||||
if x >= size {
|
||||
x ^= primitive;
|
||||
x &= size - 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size - 1 {
|
||||
{
|
||||
log_table[exp_table[i]] = i;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// logTable[0] == 0 but this should never be used
|
||||
new_generic_gf.zero = GenericGFPoly::new(0, &vec![0]);
|
||||
new_generic_gf.one = GenericGFPoly::new(0, &vec![1]);
|
||||
|
||||
new_generic_gf
|
||||
}
|
||||
|
||||
fn get_zero(&self) -> GenericGFPoly {
|
||||
return self.zero;
|
||||
}
|
||||
|
||||
fn get_one(&self) -> GenericGFPoly {
|
||||
return self.one;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
fn build_monomial(
|
||||
&self,
|
||||
degree: i32,
|
||||
coefficient: i32,
|
||||
) -> Result<GenericGFPoly, IllegalArgumentException> {
|
||||
if degree < 0 {
|
||||
return Err(IllegalArgumentException::new());
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return Ok(self.zero);
|
||||
}
|
||||
let mut coefficients: [i32; degree + 1] = [0; degree + 1];
|
||||
coefficients[0] = coefficient;
|
||||
return GenericGFPoly::new(self, &coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
fn add_or_subtract(a: i32, b: i32) -> i32 {
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
fn exp(&self, a: i32) -> i32 {
|
||||
return self.exp_table[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
fn log(&self, a: i32) -> Result<i32, IllegalArgumentException> {
|
||||
if a == 0 {
|
||||
return Err(IllegalArgumentException::new());
|
||||
}
|
||||
return self.log_table[a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
fn inverse(&self, a: i32) -> Result<i32, ArithmeticException> {
|
||||
if a == 0 {
|
||||
return Err(ArithmeticException::new());
|
||||
}
|
||||
return self.exp_table[self.size - self.log_table[a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return product of a and b in GF(size)
|
||||
*/
|
||||
fn multiply(&self, a: i32, b: i32) -> i32 {
|
||||
if a == 0 || b == 0 {
|
||||
return 0;
|
||||
}
|
||||
return self.exp_table[(self.log_table[a] + self.log_table[b]) % (self.size - 1)];
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> i32 {
|
||||
return self.size;
|
||||
}
|
||||
|
||||
pub fn get_generator_base(&self) -> i32 {
|
||||
return self.generator_base;
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
return format!(
|
||||
"GF(0x{},{})",
|
||||
Integer::to_hex_string(self.primitive),
|
||||
self.size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ReedSolomonDecoder.java
|
||||
/**
|
||||
* <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
|
||||
*/
|
||||
pub struct ReedSolomonDecoder {
|
||||
field: GenericGF,
|
||||
}
|
||||
|
||||
impl ReedSolomonDecoder {
|
||||
pub fn new(field: &GenericGF) -> Self {
|
||||
Self { 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
|
||||
*/
|
||||
pub fn decode(&self, received: &Vec<i32>, two_s: i32) -> Result<(), ReedSolomonException> {
|
||||
let poly: GenericGFPoly = GenericGFPoly::new(&self.field, &received);
|
||||
let syndrome_coefficients: [i32; two_s] = [0; two_s];
|
||||
let no_error: bool = true;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < two_s {
|
||||
{
|
||||
let eval: i32 =
|
||||
poly.evaluate_at(&self.field.exp(i + self.field.get_generator_base()));
|
||||
syndrome_coefficients[syndrome_coefficients.len() - 1 - i] = eval;
|
||||
if eval != 0 {
|
||||
no_error = false;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if no_error {
|
||||
return;
|
||||
}
|
||||
let syndrome: GenericGFPoly = GenericGFPoly::new(&self.field, &syndrome_coefficients);
|
||||
let sigma_omega: Vec<GenericGFPoly> =
|
||||
self.run_euclidean_algorithm(&self.field.build_monomial(two_s, 1), &syndrome, two_s);
|
||||
let sigma: GenericGFPoly = sigma_omega[0];
|
||||
let omega: GenericGFPoly = sigma_omega[1];
|
||||
let error_locations: Vec<i32> = self.find_error_locations(&sigma);
|
||||
let error_magnitudes: Vec<i32> = self.find_error_magnitudes(&omega, &error_locations);
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < error_locations.len() {
|
||||
{
|
||||
let mut position: i32 = received.len() - 1 - self.field.log(error_locations[i]);
|
||||
if position < 0 {
|
||||
return Err(ReedSolomonException::new("Bad error location"));
|
||||
}
|
||||
received[position] =
|
||||
GenericGF::add_or_subtract(received[position], error_magnitudes[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_euclidean_algorithm(
|
||||
&self,
|
||||
a: &GenericGFPoly,
|
||||
b: &GenericGFPoly,
|
||||
R: i32,
|
||||
) -> Result<Vec<GenericGFPoly>, ReedSolomonException + IllegalStateException> {
|
||||
// Assume a's degree is >= b's
|
||||
if a.get_degree() < b.get_degree() {
|
||||
let temp: GenericGFPoly = a;
|
||||
a = b;
|
||||
b = &temp;
|
||||
}
|
||||
let r_last: GenericGFPoly = a;
|
||||
let mut r: GenericGFPoly = b;
|
||||
let t_last: GenericGFPoly = self.field.get_zero();
|
||||
let mut t: GenericGFPoly = self.field.get_one();
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while 2 * r.get_degree() >= R {
|
||||
let r_last_last: GenericGFPoly = r_last;
|
||||
let t_last_last: GenericGFPoly = t_last;
|
||||
r_last = r;
|
||||
t_last = t;
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if r_last.is_zero() {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
return Err(ReedSolomonException::new("r_{i-1} was zero"));
|
||||
}
|
||||
r = r_last_last;
|
||||
let mut q: GenericGFPoly = self.field.get_zero();
|
||||
let denominator_leading_term: i32 = r_last.get_coefficient(&r_last.get_degree());
|
||||
let dlt_inverse: i32 = self.field.inverse(denominator_leading_term);
|
||||
while r.get_degree() >= r_last.get_degree() && !r.is_zero() {
|
||||
let degree_diff: i32 = r.get_degree() - r_last.get_degree();
|
||||
let scale: i32 = self
|
||||
.field
|
||||
.multiply(&r.get_coefficient(&r.get_degree()), dlt_inverse);
|
||||
q = q.add_or_subtract(&self.field.build_monomial(degree_diff, scale));
|
||||
r = r.add_or_subtract(&r_last.multiply_by_monomial(degree_diff, scale));
|
||||
}
|
||||
t = q.multiply(&t_last).add_or_subtract(t_last_last);
|
||||
if r.get_degree() >= r_last.get_degree() {
|
||||
return Err(IllegalStateException::new(format!(
|
||||
"Division algorithm failed to reduce polynomial? r: {}, rLast: {}",
|
||||
r, r_last
|
||||
)));
|
||||
}
|
||||
}
|
||||
let sigma_tilde_at_zero: i32 = t.get_coefficient(0);
|
||||
if sigma_tilde_at_zero == 0 {
|
||||
return Err(ReedSolomonException::new("sigmaTilde(0) was zero"));
|
||||
}
|
||||
let inverse: i32 = self.field.inverse(sigma_tilde_at_zero);
|
||||
let sigma: GenericGFPoly = t.multiply(inverse);
|
||||
let omega: GenericGFPoly = r.multiply(inverse);
|
||||
return Ok(vec![sigma, omega]);
|
||||
}
|
||||
|
||||
fn find_error_locations(
|
||||
&self,
|
||||
error_locator: &GenericGFPoly,
|
||||
) -> Result<Vec<i32>, ReedSolomonException> {
|
||||
// This is a direct application of Chien's search
|
||||
let num_errors: i32 = error_locator.get_degree();
|
||||
if num_errors == 1 {
|
||||
// shortcut
|
||||
return Ok(vec![error_locator.get_coefficient(1)]);
|
||||
}
|
||||
let mut result: [i32; num_errors] = [0; num_errors];
|
||||
let mut e: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < self.field.get_size() && e < num_errors {
|
||||
{
|
||||
if error_locator.evaluate_at(i) == 0 {
|
||||
result[e] = self.field.inverse(i);
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if e != num_errors {
|
||||
return Err(ReedSolomonException::new(
|
||||
"Error locator degree does not match number of roots",
|
||||
));
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn find_error_magnitudes(
|
||||
&self,
|
||||
error_evaluator: &GenericGFPoly,
|
||||
error_locations: &Vec<i32>,
|
||||
) -> Vec<i32> {
|
||||
// This is directly applying Forney's Formula
|
||||
let s: i32 = error_locations.len();
|
||||
let mut result: [i32; s] = [0; s];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < s {
|
||||
{
|
||||
let xi_inverse: i32 = self.field.inverse(error_locations[i]);
|
||||
let mut denominator: i32 = 1;
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < s {
|
||||
{
|
||||
if i != j {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
|
||||
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
|
||||
// Below is a funny-looking workaround from Steven Parkes
|
||||
let term: i32 =
|
||||
self.field.multiply(error_locations[j], xi_inverse);
|
||||
let term_plus1: i32 = if (term & 0x1) == 0 {
|
||||
term | 1
|
||||
} else {
|
||||
term & 1
|
||||
};
|
||||
denominator = self.field.multiply(denominator, term_plus1);
|
||||
}
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = self.field.multiply(
|
||||
&error_evaluator.evaluate_at(xi_inverse),
|
||||
&self.field.inverse(denominator),
|
||||
);
|
||||
if self.field.get_generator_base() != 0 {
|
||||
result[i] = self.field.multiply(result[i], xi_inverse);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// ReedSolomonEncoder.java
|
||||
/**
|
||||
* <p>Implements Reed-Solomon encoding, as the name implies.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author William Rucklidge
|
||||
*/
|
||||
pub struct ReedSolomonEncoder {
|
||||
field: GenericGF,
|
||||
|
||||
cached_generators: Vector<GenericGFPoly>,
|
||||
}
|
||||
|
||||
impl ReedSolomonEncoder {
|
||||
pub fn new(field: &GenericGF) -> Self {
|
||||
let mut new_rse;
|
||||
new_rse.field = field;
|
||||
new_rse.cachedGenerators = Vector::new();
|
||||
cached_generators.add(GenericGFPoly::new(field, &vec![1]));
|
||||
new_rse
|
||||
}
|
||||
|
||||
fn build_generator(&self, degree: i32) -> GenericGFPoly {
|
||||
if degree >= self.cached_generators.size() {
|
||||
let last_generator: GenericGFPoly = self
|
||||
.cached_generators
|
||||
.get(self.cached_generators.size() - 1);
|
||||
{
|
||||
let mut d: i32 = self.cached_generators.size();
|
||||
while d <= degree {
|
||||
{
|
||||
let next_generator: GenericGFPoly =
|
||||
last_generator.multiply(GenericGFPoly::new(
|
||||
&self.field,
|
||||
&vec![1, self.field.exp(d - 1 + self.field.get_generator_base())],
|
||||
));
|
||||
self.cached_generators.add(next_generator);
|
||||
last_generator = next_generator;
|
||||
}
|
||||
d += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.cached_generators.get(degree);
|
||||
}
|
||||
|
||||
pub fn encode(
|
||||
&self,
|
||||
to_encode: &Vec<i32>,
|
||||
ec_bytes: i32,
|
||||
) -> Result<(), IllegalArgumentException> {
|
||||
if ec_bytes == 0 {
|
||||
return Err(IllegalArgumentException::new("No error correction bytes"));
|
||||
}
|
||||
let data_bytes: i32 = to_encode.len() - ec_bytes;
|
||||
if data_bytes <= 0 {
|
||||
return Err(IllegalArgumentException::new("No data bytes provided"));
|
||||
}
|
||||
let generator: GenericGFPoly = self.build_generator(ec_bytes);
|
||||
let info_coefficients: [i32; data_bytes] = [0; data_bytes];
|
||||
System::arraycopy(&to_encode, 0, &info_coefficients, 0, data_bytes);
|
||||
let mut info: GenericGFPoly = GenericGFPoly::new(&self.field, &info_coefficients);
|
||||
info = info.multiply_by_monomial(ec_bytes, 1);
|
||||
let remainder: GenericGFPoly = info.divide(&generator)[1];
|
||||
let coefficients: Vec<i32> = remainder.get_coefficients();
|
||||
let num_zero_coefficients: i32 = ec_bytes - coefficients.len();
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_zero_coefficients {
|
||||
{
|
||||
to_encode[data_bytes + i] = 0;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
System::arraycopy(
|
||||
&coefficients,
|
||||
0,
|
||||
&to_encode,
|
||||
data_bytes + num_zero_coefficients,
|
||||
coefficients.len(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ReedSolomonException.java
|
||||
/**
|
||||
* <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: &String) -> Self {
|
||||
ReedSolomonException { message }
|
||||
}
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
pub mod decoder;
|
||||
pub mod detector;
|
||||
pub mod encoder;
|
||||
|
||||
use crate::common::{BitMatrix, DecoderResult, DetectorResult};
|
||||
use crate::datamatrix::decoder::Decoder;
|
||||
use crate::datamatrix::detector::Detector;
|
||||
use crate::datamatrix::encoder::{
|
||||
DefaultPlacement, ErrorCorrection, HighLevelEncoder, MinimalEncoder, SymbolInfo,
|
||||
SymbolShapeHint,
|
||||
};
|
||||
use crate::qrcode::encoder::ByteMatrix;
|
||||
use crate::{
|
||||
BarcodeFormat, BinaryBitmap, ChecksumException, DecodeHintType, Dimension, EncodeHintType,
|
||||
FormatException, NotFoundException, RXingResult, Reader, ResultMetadataType, ResultPoint,
|
||||
Writer,
|
||||
};
|
||||
|
||||
// DataMatrixReader.java
|
||||
/**
|
||||
* This implementation can detect and decode Data Matrix codes in an image.
|
||||
*
|
||||
* @author bbrown@google.com (Brian Brown)
|
||||
*/
|
||||
|
||||
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
||||
pub struct DataMatrixReader {
|
||||
decoder: Decoder,
|
||||
}
|
||||
|
||||
impl Reader for DataMatrixReader {
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
fn decode(
|
||||
&self,
|
||||
image: &BinaryBitmap,
|
||||
hints: &Map<DecodeHintType, _>,
|
||||
) -> Result<Result, NotFoundException, ChecksumException, FormatException> {
|
||||
let decoder_result: DecoderResult;
|
||||
let mut points: Vec<ResultPoint>;
|
||||
if hints != null && hints.contains_key(DecodeHintType::PURE_BARCODE) {
|
||||
let bits: BitMatrix = ::extract_pure_bits(&image.get_black_matrix());
|
||||
decoder_result = self.decoder.decode(bits);
|
||||
points = NO_POINTS;
|
||||
} else {
|
||||
let detector_result: DetectorResult = Detector::new(&image.get_black_matrix()).detect();
|
||||
decoder_result = self.decoder.decode(&detector_result.get_bits());
|
||||
points = detector_result.get_points();
|
||||
}
|
||||
let result: Result = Result::new(
|
||||
&decoder_result.get_text(),
|
||||
&decoder_result.get_raw_bytes(),
|
||||
points,
|
||||
BarcodeFormat::DATA_MATRIX,
|
||||
);
|
||||
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
||||
if byte_segments != null {
|
||||
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
||||
}
|
||||
let ec_level: String = decoder_result.get_e_c_level();
|
||||
if ec_level != null {
|
||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||
}
|
||||
result.put_metadata(
|
||||
ResultMetadataType::SYMBOLOGY_IDENTIFIER,
|
||||
format!("]d{}", decoder_result.get_symbology_modifier()),
|
||||
);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl DataMatrixReader {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
decoder: Decoder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
fn extract_pure_bits(image: &BitMatrix) -> Result<BitMatrix, NotFoundException> {
|
||||
let left_top_black: Vec<i32> = image.get_top_left_on_bit();
|
||||
let right_bottom_black: Vec<i32> = image.get_bottom_right_on_bit();
|
||||
if left_top_black == null || right_bottom_black == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let module_size: i32 = self.module_size(&left_top_black, image);
|
||||
let mut top: i32 = left_top_black[1];
|
||||
let bottom: i32 = right_bottom_black[1];
|
||||
let mut left: i32 = left_top_black[0];
|
||||
let right: i32 = right_bottom_black[0];
|
||||
let matrix_width: i32 = (right - left + 1) / module_size;
|
||||
let matrix_height: i32 = (bottom - top + 1) / module_size;
|
||||
if matrix_width <= 0 || matrix_height <= 0 {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
let nudge: i32 = module_size / 2;
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
// Now just read off the bits
|
||||
let bits: BitMatrix = BitMatrix::new(matrix_width, matrix_height);
|
||||
{
|
||||
let mut y: i32 = 0;
|
||||
while y < matrix_height {
|
||||
{
|
||||
let i_offset: i32 = top + y * module_size;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < matrix_width {
|
||||
{
|
||||
if image.get(left + x * module_size, i_offset) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(bits);
|
||||
}
|
||||
|
||||
fn module_size(left_top_black: &Vec<i32>, image: &BitMatrix) -> Result<i32, NotFoundException> {
|
||||
let width: i32 = image.get_width();
|
||||
let mut x: i32 = left_top_black[0];
|
||||
let y: i32 = left_top_black[1];
|
||||
while x < width && image.get(x, y) {
|
||||
x += 1;
|
||||
}
|
||||
if x == width {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let module_size: i32 = x - left_top_black[0];
|
||||
if module_size == 0 {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
return Ok(module_size);
|
||||
}
|
||||
}
|
||||
|
||||
// DataMatrixWriter.java
|
||||
/**
|
||||
* This object renders a Data Matrix code as a BitMatrix 2D array of greyscale values.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Guillaume Le Biller Added to zxing lib.
|
||||
*/
|
||||
pub struct DataMatrixWriter {}
|
||||
|
||||
impl Writer for DataMatrixWriter {
|
||||
fn encode(
|
||||
&self,
|
||||
contents: &String,
|
||||
format: &BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
hints: &Map<EncodeHintType, _>,
|
||||
) -> BitMatrix {
|
||||
if contents.is_empty() {
|
||||
return Err(IllegalArgumentException::new("Found empty contents"));
|
||||
}
|
||||
if format != BarcodeFormat::DATA_MATRIX {
|
||||
return Err(IllegalArgumentException::new(format!(
|
||||
"Can only encode DATA_MATRIX, but got {}",
|
||||
format
|
||||
)));
|
||||
}
|
||||
if width < 0 || height < 0 {
|
||||
return Err(IllegalArgumentException::new(format!(
|
||||
"Requested dimensions can't be negative: {}x{}",
|
||||
width, height
|
||||
)));
|
||||
}
|
||||
// Try to get force shape & min / max size
|
||||
let mut shape: SymbolShapeHint = SymbolShapeHint::FORCE_NONE;
|
||||
let min_size: Dimension = null;
|
||||
let max_size: Dimension = null;
|
||||
if hints != null {
|
||||
let requested_shape: SymbolShapeHint =
|
||||
hints.get(EncodeHintType::DATA_MATRIX_SHAPE) as SymbolShapeHint;
|
||||
if requested_shape != null {
|
||||
shape = requested_shape;
|
||||
}
|
||||
let requested_min_size: Dimension = hints.get(EncodeHintType::MIN_SIZE) as Dimension;
|
||||
if requested_min_size != null {
|
||||
min_size = requested_min_size;
|
||||
}
|
||||
let requested_max_size: Dimension = hints.get(EncodeHintType::MAX_SIZE) as Dimension;
|
||||
if requested_max_size != null {
|
||||
max_size = requested_max_size;
|
||||
}
|
||||
}
|
||||
//1. step: Data encodation
|
||||
let mut encoded: String;
|
||||
let has_compaction_hint: bool = hints != null
|
||||
&& hints.contains_key(EncodeHintType::DATA_MATRIX_COMPACT)
|
||||
&& Boolean::parse_boolean(&hints.get(EncodeHintType::DATA_MATRIX_COMPACT).to_string());
|
||||
if has_compaction_hint {
|
||||
let has_g_s1_format_hint: bool = hints.contains_key(EncodeHintType::GS1_FORMAT)
|
||||
&& Boolean::parse_boolean(&hints.get(EncodeHintType::GS1_FORMAT).to_string());
|
||||
let mut charset: Charset = null;
|
||||
let has_encoding_hint: bool = hints.contains_key(EncodeHintType::CHARACTER_SET);
|
||||
if has_encoding_hint {
|
||||
charset = Charset::for_name(&hints.get(EncodeHintType::CHARACTER_SET).to_string());
|
||||
}
|
||||
encoded = MinimalEncoder::encode_high_level(
|
||||
&contents,
|
||||
&charset,
|
||||
if has_g_s1_format_hint { 0x1D } else { -1 },
|
||||
&shape,
|
||||
);
|
||||
} else {
|
||||
let has_force_c40_hint: bool = hints != null
|
||||
&& hints.contains_key(EncodeHintType::FORCE_C40)
|
||||
&& Boolean::parse_boolean(&hints.get(EncodeHintType::FORCE_C40).to_string());
|
||||
encoded = HighLevelEncoder::encode_high_level(
|
||||
&contents,
|
||||
shape,
|
||||
min_size,
|
||||
max_size,
|
||||
has_force_c40_hint,
|
||||
);
|
||||
}
|
||||
let symbol_info: SymbolInfo =
|
||||
SymbolInfo::lookup(&encoded.length(), shape, min_size, max_size, true);
|
||||
//2. step: ECC generation
|
||||
let codewords: String = ErrorCorrection::encode_e_c_c200(&encoded, &symbol_info);
|
||||
//3. step: Module placement in Matrix
|
||||
let placement: DefaultPlacement = DefaultPlacement::new(
|
||||
&codewords,
|
||||
&symbol_info.get_symbol_data_width(),
|
||||
&symbol_info.get_symbol_data_height(),
|
||||
);
|
||||
placement.place();
|
||||
//4. step: low-level encoding
|
||||
return ::encode_low_level(placement, symbol_info, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
impl DataMatrixWriter {
|
||||
/**
|
||||
* Encode the given symbol info to a bit matrix.
|
||||
*
|
||||
* @param placement The DataMatrix placement.
|
||||
* @param symbolInfo The symbol info to encode.
|
||||
* @return The bit matrix generated.
|
||||
*/
|
||||
fn encode_low_level(
|
||||
placement: &DefaultPlacement,
|
||||
symbol_info: &SymbolInfo,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> BitMatrix {
|
||||
let symbol_width: i32 = symbol_info.get_symbol_data_width();
|
||||
let symbol_height: i32 = symbol_info.get_symbol_data_height();
|
||||
let matrix: ByteMatrix = ByteMatrix::new(
|
||||
&symbol_info.get_symbol_width(),
|
||||
&symbol_info.get_symbol_height(),
|
||||
);
|
||||
let matrix_y: i32 = 0;
|
||||
{
|
||||
let mut y: i32 = 0;
|
||||
while y < symbol_height {
|
||||
{
|
||||
// Fill the top edge with alternate 0 / 1
|
||||
let matrix_x: i32;
|
||||
if (y % symbol_info.matrixHeight) == 0 {
|
||||
matrix_x = 0;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < symbol_info.get_symbol_width() {
|
||||
{
|
||||
matrix.set(matrix_x, matrix_y, (x % 2) == 0);
|
||||
matrix_x += 1;
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_y += 1;
|
||||
}
|
||||
matrix_x = 0;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < symbol_width {
|
||||
{
|
||||
// Fill the right edge with full 1
|
||||
if (x % symbol_info.matrixWidth) == 0 {
|
||||
matrix.set(matrix_x, matrix_y, true);
|
||||
matrix_x += 1;
|
||||
}
|
||||
matrix.set(matrix_x, matrix_y, &placement.get_bit(x, y));
|
||||
matrix_x += 1;
|
||||
// Fill the right edge with alternate 0 / 1
|
||||
if (x % symbol_info.matrixWidth) == symbol_info.matrixWidth - 1 {
|
||||
matrix.set(matrix_x, matrix_y, (y % 2) == 0);
|
||||
matrix_x += 1;
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_y += 1;
|
||||
// Fill the bottom edge with full 1
|
||||
if (y % symbol_info.matrixHeight) == symbol_info.matrixHeight - 1 {
|
||||
matrix_x = 0;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < symbol_info.get_symbol_width() {
|
||||
{
|
||||
matrix.set(matrix_x, matrix_y, true);
|
||||
matrix_x += 1;
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
matrix_y += 1;
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ::convert_byte_matrix_to_bit_matrix(matrix, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the ByteMatrix to BitMatrix.
|
||||
*
|
||||
* @param reqHeight The requested height of the image (in pixels) with the Datamatrix code
|
||||
* @param reqWidth The requested width of the image (in pixels) with the Datamatrix code
|
||||
* @param matrix The input matrix.
|
||||
* @return The output matrix.
|
||||
*/
|
||||
fn convert_byte_matrix_to_bit_matrix(
|
||||
matrix: &ByteMatrix,
|
||||
req_width: i32,
|
||||
req_height: i32,
|
||||
) -> BitMatrix {
|
||||
let matrix_width: i32 = matrix.get_width();
|
||||
let matrix_height: i32 = matrix.get_height();
|
||||
let output_width: i32 = Math::max(req_width, matrix_width);
|
||||
let output_height: i32 = Math::max(req_height, matrix_height);
|
||||
let multiple: i32 = Math::min(output_width / matrix_width, output_height / matrix_height);
|
||||
let left_padding: i32 = (output_width - (matrix_width * multiple)) / 2;
|
||||
let top_padding: i32 = (output_height - (matrix_height * multiple)) / 2;
|
||||
let mut output: BitMatrix;
|
||||
// remove padding if requested width and height are too small
|
||||
if req_height < matrix_height || req_width < matrix_width {
|
||||
left_padding = 0;
|
||||
top_padding = 0;
|
||||
output = BitMatrix::new(matrix_width, matrix_height);
|
||||
} else {
|
||||
output = BitMatrix::new(req_width, req_height);
|
||||
}
|
||||
output.clear();
|
||||
{
|
||||
let input_y: i32 = 0;
|
||||
let output_y: i32 = top_padding;
|
||||
while input_y < matrix_height {
|
||||
{
|
||||
// Write the contents of this row of the bytematrix
|
||||
{
|
||||
let input_x: i32 = 0;
|
||||
let output_x: i32 = left_padding;
|
||||
while input_x < matrix_width {
|
||||
{
|
||||
if matrix.get(input_x, input_y) == 1 {
|
||||
output.set_region(output_x, output_y, multiple, multiple);
|
||||
}
|
||||
}
|
||||
input_x += 1;
|
||||
output_x += multiple;
|
||||
}
|
||||
}
|
||||
}
|
||||
input_y += 1;
|
||||
output_y += multiple;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,363 +0,0 @@
|
||||
use crate::common::detector::WhiteRectangleDetector;
|
||||
use crate::common::{BitMatrix, DetectorResult, GridSampler};
|
||||
use crate::{NotFoundException, ResultPoint};
|
||||
|
||||
// Detector.java
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
pub struct Detector {
|
||||
image: BitMatrix,
|
||||
|
||||
rectangle_detector: WhiteRectangleDetector,
|
||||
}
|
||||
|
||||
impl Detector {
|
||||
pub fn new(image: &BitMatrix) -> Result<Self, NotFoundException> {
|
||||
let d: Self;
|
||||
d.image = image;
|
||||
d.rectangle_detector = WhiteRectangleDetector::new(image, None, None, None);
|
||||
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a Data Matrix Code in an image.</p>
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
pub fn detect(&self) -> Result<DetectorResult, NotFoundException> {
|
||||
let corner_points: Vec<ResultPoint> = self.rectangle_detector.detect();
|
||||
let mut points: Vec<ResultPoint> = self.detect_solid1(&corner_points);
|
||||
points = self.detect_solid2(points?);
|
||||
points[3] = self.correct_top_right(points?);
|
||||
if points[3] == null {
|
||||
return Err(NotFoundException::get_not_found_instance());
|
||||
}
|
||||
points = self.shift_to_module_center(points?);
|
||||
let top_left: ResultPoint = points[0];
|
||||
let bottom_left: ResultPoint = points[1];
|
||||
let bottom_right: ResultPoint = points[2];
|
||||
let top_right: ResultPoint = points[3];
|
||||
let dimension_top: i32 = self.transitions_between(&top_left, &top_right) + 1;
|
||||
let dimension_right: i32 = self.transitions_between(&bottom_right, &top_right) + 1;
|
||||
if (dimension_top & 0x01) == 1 {
|
||||
dimension_top += 1;
|
||||
}
|
||||
if (dimension_right & 0x01) == 1 {
|
||||
dimension_right += 1;
|
||||
}
|
||||
if 4 * dimension_top < 6 * dimension_right && 4 * dimension_right < 6 * dimension_top {
|
||||
// The matrix is square
|
||||
dimension_top = dimension_right = Math::max(dimension_top, dimension_right);
|
||||
}
|
||||
let bits: BitMatrix = ::sample_grid(
|
||||
self.image,
|
||||
top_left,
|
||||
bottom_left,
|
||||
bottom_right,
|
||||
top_right,
|
||||
dimension_top,
|
||||
dimension_right,
|
||||
);
|
||||
return Ok(DetectorResult::new(
|
||||
bits,
|
||||
vec![top_left, bottom_left, bottom_right, top_right],
|
||||
));
|
||||
}
|
||||
|
||||
fn shift_point(point: &ResultPoint, to: &ResultPoint, div: i32) -> ResultPoint {
|
||||
let x: f32 = (to.get_x() - point.get_x()) / (div + 1);
|
||||
let y: f32 = (to.get_y() - point.get_y()) / (div + 1);
|
||||
return ResultPoint::new(point.get_x() + x, point.get_y() + y);
|
||||
}
|
||||
|
||||
fn move_away(point: &ResultPoint, from_x: f32, from_y: f32) -> ResultPoint {
|
||||
let mut x: f32 = point.get_x();
|
||||
let mut y: f32 = point.get_y();
|
||||
if x < from_x {
|
||||
x -= 1.0;
|
||||
} else {
|
||||
x += 1.0;
|
||||
}
|
||||
if y < from_y {
|
||||
y -= 1.0;
|
||||
} else {
|
||||
y += 1.0;
|
||||
}
|
||||
return ResultPoint::new(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect a solid side which has minimum transition.
|
||||
*/
|
||||
fn detect_solid1(&self, corner_points: &Vec<ResultPoint>) -> Vec<ResultPoint> {
|
||||
// 0 2
|
||||
// 1 3
|
||||
let point_a: ResultPoint = corner_points[0];
|
||||
let point_b: ResultPoint = corner_points[1];
|
||||
let point_c: ResultPoint = corner_points[3];
|
||||
let point_d: ResultPoint = corner_points[2];
|
||||
let tr_a_b: i32 = self.transitions_between(&point_a, &point_b);
|
||||
let tr_b_c: i32 = self.transitions_between(&point_b, &point_c);
|
||||
let tr_c_d: i32 = self.transitions_between(&point_c, &point_d);
|
||||
let tr_d_a: i32 = self.transitions_between(&point_d, &point_a);
|
||||
// 0..3
|
||||
// : :
|
||||
// 1--2
|
||||
let mut min: i32 = tr_a_b;
|
||||
let mut points: vec![Vec<ResultPoint>; 4] = vec![point_d, point_a, point_b, point_c];
|
||||
if min > tr_b_c {
|
||||
min = tr_b_c;
|
||||
points[0] = point_a;
|
||||
points[1] = point_b;
|
||||
points[2] = point_c;
|
||||
points[3] = point_d;
|
||||
}
|
||||
if min > tr_c_d {
|
||||
min = tr_c_d;
|
||||
points[0] = point_b;
|
||||
points[1] = point_c;
|
||||
points[2] = point_d;
|
||||
points[3] = point_a;
|
||||
}
|
||||
if min > tr_d_a {
|
||||
points[0] = point_c;
|
||||
points[1] = point_d;
|
||||
points[2] = point_a;
|
||||
points[3] = point_b;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect a second solid side next to first solid side.
|
||||
*/
|
||||
fn detect_solid2(&self, points: &Vec<ResultPoint>) -> Vec<ResultPoint> {
|
||||
// A..D
|
||||
// : :
|
||||
// B--C
|
||||
let point_a: ResultPoint = points[0];
|
||||
let point_b: ResultPoint = points[1];
|
||||
let point_c: ResultPoint = points[2];
|
||||
let point_d: ResultPoint = points[3];
|
||||
// Transition detection on the edge is not stable.
|
||||
// To safely detect, shift the points to the module center.
|
||||
let tr: i32 = self.transitions_between(&point_a, &point_d);
|
||||
let point_bs: ResultPoint = ::shift_point(point_b, point_c, (tr + 1) * 4);
|
||||
let point_cs: ResultPoint = ::shift_point(point_c, point_b, (tr + 1) * 4);
|
||||
let tr_b_a: i32 = self.transitions_between(&point_bs, &point_a);
|
||||
let tr_c_d: i32 = self.transitions_between(&point_cs, &point_d);
|
||||
// 1--2
|
||||
if tr_b_a < tr_c_d {
|
||||
// solid sides: A-B-C
|
||||
points[0] = point_a;
|
||||
points[1] = point_b;
|
||||
points[2] = point_c;
|
||||
points[3] = point_d;
|
||||
} else {
|
||||
// solid sides: B-C-D
|
||||
points[0] = point_b;
|
||||
points[1] = point_c;
|
||||
points[2] = point_d;
|
||||
points[3] = point_a;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the corner position of the white top right module.
|
||||
*/
|
||||
fn correct_top_right(&self, points: &Vec<ResultPoint>) -> ResultPoint {
|
||||
// A..D
|
||||
// | :
|
||||
// B--C
|
||||
let point_a: ResultPoint = points[0];
|
||||
let point_b: ResultPoint = points[1];
|
||||
let point_c: ResultPoint = points[2];
|
||||
let point_d: ResultPoint = points[3];
|
||||
// shift points for safe transition detection.
|
||||
let tr_top: i32 = self.transitions_between(&point_a, &point_d);
|
||||
let tr_right: i32 = self.transitions_between(&point_b, &point_d);
|
||||
let point_as: ResultPoint = ::shift_point(point_a, point_b, (tr_right + 1) * 4);
|
||||
let point_cs: ResultPoint = ::shift_point(point_c, point_b, (tr_top + 1) * 4);
|
||||
tr_top = self.transitions_between(&point_as, &point_d);
|
||||
tr_right = self.transitions_between(&point_cs, &point_d);
|
||||
let candidate1: ResultPoint = ResultPoint::new(
|
||||
point_d.get_x() + (point_c.get_x() - point_b.get_x()) / (tr_top + 1),
|
||||
point_d.get_y() + (point_c.get_y() - point_b.get_y()) / (tr_top + 1),
|
||||
);
|
||||
let candidate2: ResultPoint = ResultPoint::new(
|
||||
point_d.get_x() + (point_a.get_x() - point_b.get_x()) / (tr_right + 1),
|
||||
point_d.get_y() + (point_a.get_y() - point_b.get_y()) / (tr_right + 1),
|
||||
);
|
||||
if !self.is_valid(&candidate1) {
|
||||
if self.is_valid(&candidate2) {
|
||||
return candidate2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if !self.is_valid(&candidate2) {
|
||||
return candidate1;
|
||||
}
|
||||
let sumc1: i32 = self.transitions_between(&point_as, &candidate1)
|
||||
+ self.transitions_between(&point_cs, &candidate1);
|
||||
let sumc2: i32 = self.transitions_between(&point_as, &candidate2)
|
||||
+ self.transitions_between(&point_cs, &candidate2);
|
||||
if sumc1 > sumc2 {
|
||||
return candidate1;
|
||||
} else {
|
||||
return candidate2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift the edge points to the module center.
|
||||
*/
|
||||
fn shift_to_module_center(&self, points: &Vec<ResultPoint>) -> Vec<ResultPoint> {
|
||||
// A..D
|
||||
// | :
|
||||
// B--C
|
||||
let point_a: ResultPoint = points[0];
|
||||
let point_b: ResultPoint = points[1];
|
||||
let point_c: ResultPoint = points[2];
|
||||
let point_d: ResultPoint = points[3];
|
||||
// calculate pseudo dimensions
|
||||
let dim_h: i32 = self.transitions_between(&point_a, &point_d) + 1;
|
||||
let dim_v: i32 = self.transitions_between(&point_c, &point_d) + 1;
|
||||
// shift points for safe dimension detection
|
||||
let point_as: ResultPoint = ::shift_point(point_a, point_b, dim_v * 4);
|
||||
let point_cs: ResultPoint = ::shift_point(point_c, point_b, dim_h * 4);
|
||||
// calculate more precise dimensions
|
||||
dim_h = self.transitions_between(&point_as, &point_d) + 1;
|
||||
dim_v = self.transitions_between(&point_cs, &point_d) + 1;
|
||||
if (dim_h & 0x01) == 1 {
|
||||
dim_h += 1;
|
||||
}
|
||||
if (dim_v & 0x01) == 1 {
|
||||
dim_v += 1;
|
||||
}
|
||||
// WhiteRectangleDetector returns points inside of the rectangle.
|
||||
// I want points on the edges.
|
||||
let center_x: f32 =
|
||||
(point_a.get_x() + point_b.get_x() + point_c.get_x() + point_d.get_x()) / 4;
|
||||
let center_y: f32 =
|
||||
(point_a.get_y() + point_b.get_y() + point_c.get_y() + point_d.get_y()) / 4;
|
||||
point_a = ::move_away(point_a, center_x, center_y);
|
||||
point_b = ::move_away(point_b, center_x, center_y);
|
||||
point_c = ::move_away(point_c, center_x, center_y);
|
||||
point_d = ::move_away(point_d, center_x, center_y);
|
||||
let point_bs: ResultPoint;
|
||||
let point_ds: ResultPoint;
|
||||
// shift points to the center of each modules
|
||||
point_as = ::shift_point(point_a, point_b, dim_v * 4);
|
||||
point_as = ::shift_point(point_as, point_d, dim_h * 4);
|
||||
point_bs = ::shift_point(point_b, point_a, dim_v * 4);
|
||||
point_bs = ::shift_point(point_bs, point_c, dim_h * 4);
|
||||
point_cs = ::shift_point(point_c, point_d, dim_v * 4);
|
||||
point_cs = ::shift_point(point_cs, point_b, dim_h * 4);
|
||||
point_ds = ::shift_point(point_d, point_c, dim_v * 4);
|
||||
point_ds = ::shift_point(point_ds, point_a, dim_h * 4);
|
||||
return vec![point_as, point_bs, point_cs, point_ds];
|
||||
}
|
||||
|
||||
fn is_valid(&self, p: &ResultPoint) -> bool {
|
||||
return p.get_x() >= 0
|
||||
&& p.get_x() <= self.image.get_width() - 1
|
||||
&& p.get_y() > 0
|
||||
&& p.get_y() <= self.image.get_height() - 1;
|
||||
}
|
||||
|
||||
fn sample_grid(
|
||||
image: &BitMatrix,
|
||||
top_left: &ResultPoint,
|
||||
bottom_left: &ResultPoint,
|
||||
bottom_right: &ResultPoint,
|
||||
top_right: &ResultPoint,
|
||||
dimension_x: i32,
|
||||
dimension_y: i32,
|
||||
) -> Result<BitMatrix, Rc<Exception>> {
|
||||
let sampler: GridSampler = GridSampler::get_instance();
|
||||
return Ok(sampler.sample_grid(
|
||||
image,
|
||||
dimension_x,
|
||||
dimension_y,
|
||||
0.5f32,
|
||||
0.5f32,
|
||||
dimension_x - 0.5f32,
|
||||
0.5f32,
|
||||
dimension_x - 0.5f32,
|
||||
dimension_y - 0.5f32,
|
||||
0.5f32,
|
||||
dimension_y - 0.5f32,
|
||||
&top_left.get_x(),
|
||||
&top_left.get_y(),
|
||||
&top_right.get_x(),
|
||||
&top_right.get_y(),
|
||||
&bottom_right.get_x(),
|
||||
&bottom_right.get_y(),
|
||||
&bottom_left.get_x(),
|
||||
&bottom_left.get_y(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
|
||||
*/
|
||||
fn transitions_between(&self, from: &ResultPoint, to: &ResultPoint) -> i32 {
|
||||
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
|
||||
let from_x: i32 = from.get_x() as i32;
|
||||
let from_y: i32 = from.get_y() as i32;
|
||||
let to_x: i32 = to.get_x() as i32;
|
||||
let to_y: i32 = Math::min(self.image.get_height() - 1, to.get_y() as i32);
|
||||
let steep: bool = Math::abs(to_y - from_y) > Math::abs(to_x - from_x);
|
||||
if steep {
|
||||
let mut temp: i32 = from_x;
|
||||
from_x = from_y;
|
||||
from_y = temp;
|
||||
temp = to_x;
|
||||
to_x = to_y;
|
||||
to_y = temp;
|
||||
}
|
||||
let dx: i32 = Math::abs(to_x - from_x);
|
||||
let dy: i32 = Math::abs(to_y - from_y);
|
||||
let mut error: i32 = -dx / 2;
|
||||
let ystep: i32 = if from_y < to_y { 1 } else { -1 };
|
||||
let xstep: i32 = if from_x < to_x { 1 } else { -1 };
|
||||
let mut transitions: i32 = 0;
|
||||
let in_black: bool = self.image.get(
|
||||
if steep { from_y } else { from_x },
|
||||
if steep { from_x } else { from_y },
|
||||
);
|
||||
{
|
||||
let mut x: i32 = from_x;
|
||||
let mut y: i32 = from_y;
|
||||
while x != to_x {
|
||||
{
|
||||
let is_black: bool = self
|
||||
.image
|
||||
.get(if steep { y } else { x }, if steep { x } else { y });
|
||||
if is_black != in_black {
|
||||
transitions += 1;
|
||||
in_black = is_black;
|
||||
}
|
||||
error += dy;
|
||||
if error > 0 {
|
||||
if y == to_y {
|
||||
break;
|
||||
}
|
||||
y += ystep;
|
||||
error -= dx;
|
||||
}
|
||||
}
|
||||
x += xstep;
|
||||
}
|
||||
}
|
||||
|
||||
return transitions;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
102
src/maxicode.rs
102
src/maxicode.rs
@@ -1,102 +0,0 @@
|
||||
pub mod decoder;
|
||||
|
||||
use crate::{BarcodeFormat,BinaryBitmap,ChecksumException,DecodeHintType,FormatException,NotFoundException,Reader,XRingResultResultMetadataType,ResultPoint};
|
||||
use crate::common::{BitMatrix,DecoderResult};
|
||||
use crate::maxicode::decoder::Decoder;
|
||||
|
||||
// MaxiCodeReader.java
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode a MaxiCode in an image.
|
||||
*/
|
||||
|
||||
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
||||
|
||||
const MATRIX_WIDTH: i32 = 30;
|
||||
|
||||
const MATRIX_HEIGHT: i32 = 33;
|
||||
pub struct MaxiCodeReader {
|
||||
|
||||
decoder: Decoder
|
||||
}
|
||||
|
||||
impl Reader for MaxiCodeReader{
|
||||
/**
|
||||
* Locates and decodes a MaxiCode in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the MaxiCode
|
||||
* @throws NotFoundException if a MaxiCode cannot be found
|
||||
* @throws FormatException if a MaxiCode cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
|
||||
fn decode(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, _>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||
// Note that MaxiCode reader effectively always assumes PURE_BARCODE mode
|
||||
// and can't detect it in an image
|
||||
let bits: BitMatrix = ::extract_pure_bits(&image.get_black_matrix());
|
||||
let decoder_result: DecoderResult = self.decoder.decode(&bits, &hints);
|
||||
let result: Result = Result::new(&decoder_result.get_text(), &decoder_result.get_raw_bytes(), NO_POINTS, BarcodeFormat::MAXICODE);
|
||||
let ec_level: String = decoder_result.get_e_c_level();
|
||||
if ec_level != null {
|
||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxiCodeReader {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self { decoder: Decoder::new() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
fn extract_pure_bits( image: &BitMatrix) -> Result<BitMatrix,NotFoundException> {
|
||||
let enclosing_rectangle: Vec<i32> = image.get_enclosing_rectangle();
|
||||
if enclosing_rectangle == null {
|
||||
return Err( NotFoundException::get_not_found_instance());
|
||||
}
|
||||
let left: i32 = enclosing_rectangle[0];
|
||||
let top: i32 = enclosing_rectangle[1];
|
||||
let width: i32 = enclosing_rectangle[2];
|
||||
let height: i32 = enclosing_rectangle[3];
|
||||
// Now just read off the bits
|
||||
let bits: BitMatrix = BitMatrix::new(MATRIX_WIDTH, MATRIX_HEIGHT);
|
||||
{
|
||||
let mut y: i32 = 0;
|
||||
while y < MATRIX_HEIGHT {
|
||||
{
|
||||
let iy: i32 = Math::min(top + (y * height + height / 2) / MATRIX_HEIGHT, height - 1);
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < MATRIX_WIDTH {
|
||||
{
|
||||
// srowen: I don't quite understand why the formula below is necessary, but it
|
||||
// can walk off the image if left + width = the right boundary. So cap it.
|
||||
let ix: i32 = left + Math::min((x * width + width / 2 + (y & 0x01) * width / 2) / MATRIX_WIDTH, width - 1);
|
||||
if image.get(ix, iy) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(bits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,450 +0,0 @@
|
||||
use crate::common::{BitMatrix,DecoderResult};
|
||||
use crate::{FormatException,ChecksumException,DecodeHintType,FormatException};
|
||||
use crate::common::reedsolomon::{GenericGF,ReedSolomonDecoder,ReedSolomonException};
|
||||
|
||||
|
||||
// BitMatrixParser.java
|
||||
/**
|
||||
* @author mike32767
|
||||
* @author Manuel Kasten
|
||||
*/
|
||||
|
||||
const BITNR: vec![vec![Vec<Vec<i32>>; 30]; 33] = vec![vec![121, 120, 127, 126, 133, 132, 139, 138, 145, 144, 151, 150, 157, 156, 163, 162, 169, 168, 175, 174, 181, 180, 187, 186, 193, 192, 199, 198, -2, -2, ]
|
||||
, vec![123, 122, 129, 128, 135, 134, 141, 140, 147, 146, 153, 152, 159, 158, 165, 164, 171, 170, 177, 176, 183, 182, 189, 188, 195, 194, 201, 200, 816, -3, ]
|
||||
, vec![125, 124, 131, 130, 137, 136, 143, 142, 149, 148, 155, 154, 161, 160, 167, 166, 173, 172, 179, 178, 185, 184, 191, 190, 197, 196, 203, 202, 818, 817, ]
|
||||
, vec![283, 282, 277, 276, 271, 270, 265, 264, 259, 258, 253, 252, 247, 246, 241, 240, 235, 234, 229, 228, 223, 222, 217, 216, 211, 210, 205, 204, 819, -3, ]
|
||||
, vec![285, 284, 279, 278, 273, 272, 267, 266, 261, 260, 255, 254, 249, 248, 243, 242, 237, 236, 231, 230, 225, 224, 219, 218, 213, 212, 207, 206, 821, 820, ]
|
||||
, vec![287, 286, 281, 280, 275, 274, 269, 268, 263, 262, 257, 256, 251, 250, 245, 244, 239, 238, 233, 232, 227, 226, 221, 220, 215, 214, 209, 208, 822, -3, ]
|
||||
, vec![289, 288, 295, 294, 301, 300, 307, 306, 313, 312, 319, 318, 325, 324, 331, 330, 337, 336, 343, 342, 349, 348, 355, 354, 361, 360, 367, 366, 824, 823, ]
|
||||
, vec![291, 290, 297, 296, 303, 302, 309, 308, 315, 314, 321, 320, 327, 326, 333, 332, 339, 338, 345, 344, 351, 350, 357, 356, 363, 362, 369, 368, 825, -3, ]
|
||||
, vec![293, 292, 299, 298, 305, 304, 311, 310, 317, 316, 323, 322, 329, 328, 335, 334, 341, 340, 347, 346, 353, 352, 359, 358, 365, 364, 371, 370, 827, 826, ]
|
||||
, vec![409, 408, 403, 402, 397, 396, 391, 390, 79, 78, -2, -2, 13, 12, 37, 36, 2, -1, 44, 43, 109, 108, 385, 384, 379, 378, 373, 372, 828, -3, ]
|
||||
, vec![411, 410, 405, 404, 399, 398, 393, 392, 81, 80, 40, -2, 15, 14, 39, 38, 3, -1, -1, 45, 111, 110, 387, 386, 381, 380, 375, 374, 830, 829, ]
|
||||
, vec![413, 412, 407, 406, 401, 400, 395, 394, 83, 82, 41, -3, -3, -3, -3, -3, 5, 4, 47, 46, 113, 112, 389, 388, 383, 382, 377, 376, 831, -3, ]
|
||||
, vec![415, 414, 421, 420, 427, 426, 103, 102, 55, 54, 16, -3, -3, -3, -3, -3, -3, -3, 20, 19, 85, 84, 433, 432, 439, 438, 445, 444, 833, 832, ]
|
||||
, vec![417, 416, 423, 422, 429, 428, 105, 104, 57, 56, -3, -3, -3, -3, -3, -3, -3, -3, 22, 21, 87, 86, 435, 434, 441, 440, 447, 446, 834, -3, ]
|
||||
, vec![419, 418, 425, 424, 431, 430, 107, 106, 59, 58, -3, -3, -3, -3, -3, -3, -3, -3, -3, 23, 89, 88, 437, 436, 443, 442, 449, 448, 836, 835, ]
|
||||
, vec![481, 480, 475, 474, 469, 468, 48, -2, 30, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 0, 53, 52, 463, 462, 457, 456, 451, 450, 837, -3, ]
|
||||
, vec![483, 482, 477, 476, 471, 470, 49, -1, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -1, 465, 464, 459, 458, 453, 452, 839, 838, ]
|
||||
, vec![485, 484, 479, 478, 473, 472, 51, 50, 31, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 1, -2, 42, 467, 466, 461, 460, 455, 454, 840, -3, ]
|
||||
, vec![487, 486, 493, 492, 499, 498, 97, 96, 61, 60, -3, -3, -3, -3, -3, -3, -3, -3, -3, 26, 91, 90, 505, 504, 511, 510, 517, 516, 842, 841, ]
|
||||
, vec![489, 488, 495, 494, 501, 500, 99, 98, 63, 62, -3, -3, -3, -3, -3, -3, -3, -3, 28, 27, 93, 92, 507, 506, 513, 512, 519, 518, 843, -3, ]
|
||||
, vec![491, 490, 497, 496, 503, 502, 101, 100, 65, 64, 17, -3, -3, -3, -3, -3, -3, -3, 18, 29, 95, 94, 509, 508, 515, 514, 521, 520, 845, 844, ]
|
||||
, vec![559, 558, 553, 552, 547, 546, 541, 540, 73, 72, 32, -3, -3, -3, -3, -3, -3, 10, 67, 66, 115, 114, 535, 534, 529, 528, 523, 522, 846, -3, ]
|
||||
, vec![561, 560, 555, 554, 549, 548, 543, 542, 75, 74, -2, -1, 7, 6, 35, 34, 11, -2, 69, 68, 117, 116, 537, 536, 531, 530, 525, 524, 848, 847, ]
|
||||
, vec![563, 562, 557, 556, 551, 550, 545, 544, 77, 76, -2, 33, 9, 8, 25, 24, -1, -2, 71, 70, 119, 118, 539, 538, 533, 532, 527, 526, 849, -3, ]
|
||||
, vec![565, 564, 571, 570, 577, 576, 583, 582, 589, 588, 595, 594, 601, 600, 607, 606, 613, 612, 619, 618, 625, 624, 631, 630, 637, 636, 643, 642, 851, 850, ]
|
||||
, vec![567, 566, 573, 572, 579, 578, 585, 584, 591, 590, 597, 596, 603, 602, 609, 608, 615, 614, 621, 620, 627, 626, 633, 632, 639, 638, 645, 644, 852, -3, ]
|
||||
, vec![569, 568, 575, 574, 581, 580, 587, 586, 593, 592, 599, 598, 605, 604, 611, 610, 617, 616, 623, 622, 629, 628, 635, 634, 641, 640, 647, 646, 854, 853, ]
|
||||
, vec![727, 726, 721, 720, 715, 714, 709, 708, 703, 702, 697, 696, 691, 690, 685, 684, 679, 678, 673, 672, 667, 666, 661, 660, 655, 654, 649, 648, 855, -3, ]
|
||||
, vec![729, 728, 723, 722, 717, 716, 711, 710, 705, 704, 699, 698, 693, 692, 687, 686, 681, 680, 675, 674, 669, 668, 663, 662, 657, 656, 651, 650, 857, 856, ]
|
||||
, vec![731, 730, 725, 724, 719, 718, 713, 712, 707, 706, 701, 700, 695, 694, 689, 688, 683, 682, 677, 676, 671, 670, 665, 664, 659, 658, 653, 652, 858, -3, ]
|
||||
, vec![733, 732, 739, 738, 745, 744, 751, 750, 757, 756, 763, 762, 769, 768, 775, 774, 781, 780, 787, 786, 793, 792, 799, 798, 805, 804, 811, 810, 860, 859, ]
|
||||
, vec![735, 734, 741, 740, 747, 746, 753, 752, 759, 758, 765, 764, 771, 770, 777, 776, 783, 782, 789, 788, 795, 794, 801, 800, 807, 806, 813, 812, 861, -3, ]
|
||||
, vec![737, 736, 743, 742, 749, 748, 755, 754, 761, 760, 767, 766, 773, 772, 779, 778, 785, 784, 791, 790, 797, 796, 803, 802, 809, 808, 815, 814, 863, 862, ]
|
||||
, ]
|
||||
;
|
||||
struct BitMatrixParser {
|
||||
|
||||
bit_matrix: BitMatrix
|
||||
}
|
||||
|
||||
impl BitMatrixParser {
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
*/
|
||||
fn new( bit_matrix: &BitMatrix) -> Self {
|
||||
Self {
|
||||
bit_matrix
|
||||
}
|
||||
}
|
||||
|
||||
fn read_codewords(&self) -> Vec<i8> {
|
||||
let mut result: [i8; 144] = [0; 144];
|
||||
let height: i32 = self.bit_matrix.get_height();
|
||||
let width: i32 = self.bit_matrix.get_width();
|
||||
{
|
||||
let mut y: i32 = 0;
|
||||
while y < height {
|
||||
{
|
||||
let bitnr_row: Vec<i32> = BITNR[y];
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < width {
|
||||
{
|
||||
let mut bit: i32 = bitnr_row[x];
|
||||
if bit >= 0 && self.bit_matrix.get(x, y) {
|
||||
result[bit / 6] |= (1 << (5 - (bit % 6))) as i8;
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DecodedBitStreamParser.java
|
||||
|
||||
/**
|
||||
* <p>MaxiCodes can encode text or structured information as bits in one of several modes,
|
||||
* with multiple character sets in one code. This class decodes the bits back into text.</p>
|
||||
*
|
||||
* @author mike32767
|
||||
* @author Manuel Kasten
|
||||
*/
|
||||
|
||||
const SHIFTA: char = '\u{FFF0}';
|
||||
|
||||
const SHIFTB: char = '\u{FFF1}';
|
||||
|
||||
const SHIFTC: char = '\u{FFF2}';
|
||||
|
||||
const SHIFTD: char = '\u{FFF3}';
|
||||
|
||||
const SHIFTE: char = '\u{FFF4}';
|
||||
|
||||
const TWOSHIFTA: char ='\u{FFF5}';
|
||||
|
||||
const THREESHIFTA: char ='\u{FFF6}';
|
||||
|
||||
const LATCHA: char = '\u{FFF7}';
|
||||
|
||||
const LATCHB: char = '\u{FFF8}';
|
||||
|
||||
const LOCK: char = '\u{FFF9}';
|
||||
|
||||
const ECI: char ='\u{FFFA}';
|
||||
|
||||
const NS: char ='\u{FFFB}';
|
||||
|
||||
const PAD: char = '\u{FFFC}';
|
||||
|
||||
const FS: char = '\u{001C}';
|
||||
|
||||
const GS: char = '\u{001D}';
|
||||
|
||||
const RS: char = '\u{001E}';
|
||||
|
||||
const COUNTRY_BYTES: vec![Vec<i8>; 10] = vec![53, 54, 43, 44, 45, 46, 47, 48, 37, 38, ]
|
||||
;
|
||||
|
||||
const SERVICE_CLASS_BYTES: vec![Vec<i8>; 10] = vec![55, 56, 57, 58, 59, 60, 49, 50, 51, 52, ]
|
||||
;
|
||||
|
||||
const POSTCODE_2_LENGTH_BYTES: vec![Vec<i8>; 6] = vec![39, 40, 41, 42, 31, 32, ]
|
||||
;
|
||||
|
||||
const POSTCODE_2_BYTES: vec![Vec<i8>; 30] = vec![33, 34, 35, 36, 25, 26, 27, 28, 29, 30, 19, 20, 21, 22, 23, 24, 13, 14, 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 1, 2, ]
|
||||
;
|
||||
|
||||
const POSTCODE_3_BYTES: vec![vec![Vec<Vec<i8>>; 6]; 6] = vec![vec![39, 40, 41, 42, 31, 32, ]
|
||||
, vec![33, 34, 35, 36, 25, 26, ]
|
||||
, vec![27, 28, 29, 30, 19, 20, ]
|
||||
, vec![21, 22, 23, 24, 13, 14, ]
|
||||
, vec![15, 16, 17, 18, 7, 8, ]
|
||||
, vec![9, 10, 11, 12, 1, 2, ]
|
||||
, ]
|
||||
;
|
||||
|
||||
|
||||
const SETS: vec![Vec<String>; 5] = vec![
|
||||
format!("\rABCDEFGHIJKLMNOPQRSTUVWXYZ{}{}{}{}{} {}\"#$%&'()*+,-./0123456789:{}{}{}{}{}" , ECI , FS , GS , RS , NS , PAD ,SHIFTB , SHIFTC , SHIFTD , SHIFTE , LATCHB),
|
||||
format!("`abcdefghijklmnopqrstuvwxyz{}{}{}{}{}\{{}\}{}{}{}{}{}{}{}{}{}", ECI, FS, GS , RS , NS , PAD ,PAD , TWOSHIFTA , THREESHIFTA , PAD ,SHIFTA , SHIFTC , SHIFTD, SHIFTE , LATCHA),
|
||||
format!("\u{00C0}\u{00C1}\u{00C2}\u{00C3}\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA{}{}{}{}{}\u00DB\u00DC\u00DD\u00DE\u00DF\u00AA\u00AC\u00B1\u00B2\u00B3\u00B5\u00B9\u00BA\u00BC\u00BD\u00BE\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089{} {}{}{}{}",ECI , FS , GS , RS , NS ,LATCHA , LOCK , SHIFTD , SHIFTE , LATCHB),
|
||||
format!("\u{00E0}\u{00E1}\u{00E2}\u{00E3}\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA{}{}{}{}{}\u00FB\u00FC\u00FD\u00FE\u00FF\u00A1\u00A8\u00AB\u00AF\u00B0\u00B4\u00B7\u00B8\u00BB\u00BF\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094{} {}{}{}{}" ,ECI , FS , GS, RS , NS ,LATCHA , SHIFTC , LOCK , SHIFTE , LATCHB),
|
||||
format!("\u{0000}\u{0001}\u{0002}\u{0003}\u0004\u0005\u0006\u0007\u0008\u0009\n\u000B\u000C\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A{}{}{}\u001B{}{}{}{}\u001F\u009F\u00A0\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A9\u00AD\u00AE\u00B6\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E{} {}{}{}{}" ,ECI , PAD, PAD , NS , FS , GS , RS , LATCHA , SHIFTC , SHIFTD , LOCK , LATCHB)
|
||||
];
|
||||
|
||||
struct DecodedBitStreamParser {
|
||||
}
|
||||
|
||||
impl DecodedBitStreamParser {
|
||||
|
||||
fn new() -> DecodedBitStreamParser {
|
||||
}
|
||||
|
||||
fn decode( bytes: &Vec<i8>, mode: i32) -> /* throws FormatException */Result<DecoderResult, Rc<Exception>> {
|
||||
let result: StringBuilder = StringBuilder::new(144);
|
||||
match mode {
|
||||
2 =>
|
||||
{
|
||||
}
|
||||
3 =>
|
||||
{
|
||||
let mut postcode: String;
|
||||
if mode == 2 {
|
||||
let pc: i32 = ::get_post_code2(&bytes);
|
||||
let ps2_length: i32 = ::get_post_code2_length(&bytes);
|
||||
if ps2_length > 10 {
|
||||
return Err( FormatException::get_format_instance());
|
||||
}
|
||||
let df: NumberFormat = DecimalFormat::new(&"0000000000".substring(0, ps2_length));
|
||||
postcode = df.format(pc);
|
||||
} else {
|
||||
postcode = ::get_post_code3(&bytes);
|
||||
}
|
||||
let three_digits: NumberFormat = DecimalFormat::new("000");
|
||||
let country: String = three_digits.format(&::get_country(&bytes));
|
||||
let service: String = three_digits.format(&::get_service_class(&bytes));
|
||||
result.append(&::get_message(&bytes, 10, 84));
|
||||
if result.to_string().starts_with(format!("[)>{}01{}", RS, GS)) {
|
||||
result.insert(9, format!("{}{}{}{}{}{}", postcode, GS, country, GS, service, GS));
|
||||
} else {
|
||||
result.insert(0, format!("{}{}{}{}{}{}", postcode, GS, country, GS, service, GS));
|
||||
}
|
||||
}
|
||||
4 =>
|
||||
{
|
||||
result.append(&::get_message(&bytes, 1, 93));
|
||||
}
|
||||
5 =>
|
||||
{
|
||||
result.append(&::get_message(&bytes, 1, 77));
|
||||
}
|
||||
}
|
||||
return Ok(DecoderResult::new(&bytes, &result.to_string(), null, &String::value_of(mode), None, None, None));
|
||||
}
|
||||
|
||||
fn get_bit( bit: i32, bytes: &Vec<i8>) -> i32 {
|
||||
bit -= 1;
|
||||
return if (bytes[bit / 6] & (1 << (5 - (bit % 6)))) == 0 { 0 } else { 1 };
|
||||
}
|
||||
|
||||
fn get_int( bytes: &Vec<i8>, x: &Vec<i8>) -> i32 {
|
||||
let mut val: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < x.len() {
|
||||
{
|
||||
val += ::get_bit(x[i], &bytes) << (x.len() - i - 1);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
fn get_country( bytes: &Vec<i8>) -> i32 {
|
||||
return ::get_int(&bytes, &COUNTRY_BYTES);
|
||||
}
|
||||
|
||||
fn get_service_class( bytes: &Vec<i8>) -> i32 {
|
||||
return ::get_int(&bytes, &SERVICE_CLASS_BYTES);
|
||||
}
|
||||
|
||||
fn get_post_code2_length( bytes: &Vec<i8>) -> i32 {
|
||||
return ::get_int(&bytes, &POSTCODE_2_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
fn get_post_code2( bytes: &Vec<i8>) -> i32 {
|
||||
return ::get_int(&bytes, &POSTCODE_2_BYTES);
|
||||
}
|
||||
|
||||
fn get_post_code3( bytes: &Vec<i8>) -> String {
|
||||
let sb: StringBuilder = StringBuilder::new(POSTCODE_3_BYTES.len());
|
||||
for p3bytes in POSTCODE_3_BYTES {
|
||||
sb.append(&SETS[0].char_at(&::get_int(&bytes, &p3bytes)));
|
||||
}
|
||||
return sb.to_string();
|
||||
}
|
||||
|
||||
fn get_message( bytes: &Vec<i8>, start: i32, len: i32) -> String {
|
||||
let sb: StringBuilder = StringBuilder::new();
|
||||
let mut shift: i32 = -1;
|
||||
let mut set: i32 = 0;
|
||||
let mut lastset: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = start;
|
||||
while i < start + len {
|
||||
{
|
||||
let c: char = SETS[set].char_at(bytes[i]);
|
||||
match c {
|
||||
LATCHA =>
|
||||
{
|
||||
set = 0;
|
||||
shift = -1;
|
||||
break;
|
||||
}
|
||||
LATCHB =>
|
||||
{
|
||||
set = 1;
|
||||
shift = -1;
|
||||
break;
|
||||
}
|
||||
SHIFTA =>
|
||||
{
|
||||
}
|
||||
SHIFTB =>
|
||||
{
|
||||
}
|
||||
SHIFTC =>
|
||||
{
|
||||
}
|
||||
SHIFTD =>
|
||||
{
|
||||
}
|
||||
SHIFTE =>
|
||||
{
|
||||
lastset = set;
|
||||
set = c - SHIFTA;
|
||||
shift = 1;
|
||||
break;
|
||||
}
|
||||
TWOSHIFTA =>
|
||||
{
|
||||
lastset = set;
|
||||
set = 0;
|
||||
shift = 2;
|
||||
break;
|
||||
}
|
||||
THREESHIFTA =>
|
||||
{
|
||||
lastset = set;
|
||||
set = 0;
|
||||
shift = 3;
|
||||
break;
|
||||
}
|
||||
NS =>
|
||||
{
|
||||
let nsval: i32 = (bytes[i += 1] << 24) + (bytes[i += 1] << 18) + (bytes[i += 1] << 12) + (bytes[i += 1] << 6) + bytes[i += 1];
|
||||
sb.append(&DecimalFormat::new("000000000").format(nsval));
|
||||
break;
|
||||
}
|
||||
LOCK =>
|
||||
{
|
||||
shift = -1;
|
||||
break;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
if shift -= 1 == 0 {
|
||||
set = lastset;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
while sb.length() > 0 && sb.char_at(sb.length() - 1) == PAD {
|
||||
sb.set_length(sb.length() - 1);
|
||||
}
|
||||
return sb.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder.java
|
||||
/**
|
||||
* <p>The main class which implements MaxiCode decoding -- as opposed to locating and extracting
|
||||
* the MaxiCode from an image.</p>
|
||||
*
|
||||
* @author Manuel Kasten
|
||||
*/
|
||||
|
||||
const ALL: i32 = 0;
|
||||
|
||||
const EVEN: i32 = 1;
|
||||
|
||||
const ODD: i32 = 2;
|
||||
pub struct Decoder {
|
||||
|
||||
rs_decoder: ReedSolomonDecoder
|
||||
}
|
||||
|
||||
impl Decoder {
|
||||
|
||||
pub fn new() -> Decoder {
|
||||
rs_decoder = ReedSolomonDecoder::new(GenericGF::MAXICODE_FIELD_64);
|
||||
}
|
||||
|
||||
pub fn decode_simple(&self, bits: &BitMatrix) -> Result<DecoderResult, ChecksumException+ FormatException> {
|
||||
return Ok(self.decode(bits, null));
|
||||
}
|
||||
|
||||
pub fn decode(&self, bits: &BitMatrix, hints: &Map<DecodeHintType, _>) -> Result<DecoderResult, ChecksumException+ FormatException> {
|
||||
let parser: BitMatrixParser = BitMatrixParser::new(bits);
|
||||
let codewords: Vec<i8> = parser.read_codewords();
|
||||
self.correct_errors(&codewords, 0, 10, 10, ALL);
|
||||
let mode: i32 = codewords[0] & 0x0F;
|
||||
let mut datawords: Vec<i8>;
|
||||
match mode {
|
||||
2 =>
|
||||
{
|
||||
}
|
||||
3 =>
|
||||
{
|
||||
}
|
||||
4 =>
|
||||
{
|
||||
self.correct_errors(&codewords, 20, 84, 40, EVEN);
|
||||
self.correct_errors(&codewords, 20, 84, 40, ODD);
|
||||
datawords = [0; 94];
|
||||
}
|
||||
5 =>
|
||||
{
|
||||
self.correct_errors(&codewords, 20, 68, 56, EVEN);
|
||||
self.correct_errors(&codewords, 20, 68, 56, ODD);
|
||||
datawords = [0; 78];
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
return Err( FormatException::get_format_instance());
|
||||
}
|
||||
}
|
||||
System::arraycopy(&codewords, 0, &datawords, 0, 10);
|
||||
System::arraycopy(&codewords, 20, &datawords, 10, datawords.len() - 10);
|
||||
return Ok(DecodedBitStreamParser::decode(&datawords, mode));
|
||||
}
|
||||
|
||||
fn correct_errors(&self, codeword_bytes: &Vec<i8>, start: i32, data_codewords: i32, ec_codewords: i32, mode: i32) -> Result<(), ChecksumException> {
|
||||
let codewords: i32 = data_codewords + ec_codewords;
|
||||
// in EVEN or ODD mode only half the codewords
|
||||
let mut divisor: i32 = if mode == ALL { 1 } else { 2 };
|
||||
// First read into an array of ints
|
||||
let codewords_ints: [i32; codewords / divisor] = [0; codewords / divisor];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < codewords {
|
||||
{
|
||||
if (mode == ALL) || (i % 2 == (mode - 1)) {
|
||||
codewords_ints[i / divisor] = codeword_bytes[i + start] & 0xFF;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
self.rs_decoder.decode(&codewords_ints, ec_codewords / divisor);
|
||||
|
||||
|
||||
// We don't care about errors in the error-correction codewords
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < data_codewords {
|
||||
{
|
||||
if (mode == ALL) || (i % 2 == (mode - 1)) {
|
||||
codeword_bytes[i + start] = codewords_ints[i / divisor] as i8;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
285
src/multi.rs
285
src/multi.rs
@@ -1,285 +0,0 @@
|
||||
use crate::{BinaryBitmap,DecodeHintType,NotFoundException,RXingResult,Reader,ReaderException,ResultPoint,ChecksumException,FormatException};
|
||||
|
||||
// ByQuadrantReader.java
|
||||
/**
|
||||
* This class attempts to decode a barcode from an image, not by scanning the whole image,
|
||||
* but by scanning subsets of the image. This is important when there may be multiple barcodes in
|
||||
* an image, and detecting a barcode may find parts of multiple barcode and fail to decode
|
||||
* (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center
|
||||
* 'quadrant' to cover the case where a barcode is found in the center.
|
||||
*
|
||||
* @see GenericMultipleBarcodeReader
|
||||
*/
|
||||
pub struct ByQuadrantReader {
|
||||
|
||||
let delegate: Reader;
|
||||
}
|
||||
|
||||
impl Reader for ByQuadrantReader {
|
||||
pub fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||
return Ok(self.decode(image, null));
|
||||
}
|
||||
|
||||
pub fn decode(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||
let width: i32 = image.get_width();
|
||||
let height: i32 = image.get_height();
|
||||
let half_width: i32 = width / 2;
|
||||
let half_height: i32 = height / 2;
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
// No need to call makeAbsolute as results will be relative to original top left here
|
||||
return Ok(self.delegate.decode(&image.crop(0, 0, half_width, half_height), &hints));
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( re: &NotFoundException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let result: Result = self.delegate.decode(&image.crop(half_width, 0, half_width, half_height), &hints);
|
||||
::make_absolute(&result.get_result_points(), half_width, 0);
|
||||
return Ok(result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( re: &NotFoundException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let result: Result = self.delegate.decode(&image.crop(0, half_height, half_width, half_height), &hints);
|
||||
::make_absolute(&result.get_result_points(), 0, half_height);
|
||||
return Ok(result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( re: &NotFoundException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let result: Result = self.delegate.decode(&image.crop(half_width, half_height, half_width, half_height), &hints);
|
||||
::make_absolute(&result.get_result_points(), half_width, half_height);
|
||||
return Ok(result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( re: &NotFoundException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
let quarter_width: i32 = half_width / 2;
|
||||
let quarter_height: i32 = half_height / 2;
|
||||
let center: BinaryBitmap = image.crop(quarter_width, quarter_height, half_width, half_height);
|
||||
let result: Result = self.delegate.decode(center, &hints);
|
||||
::make_absolute(&result.get_result_points(), quarter_width, quarter_height);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
self.delegate.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl ByQuadrantReader {
|
||||
|
||||
pub fn new( delegate: &Reader) -> ByQuadrantReader {
|
||||
let .delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn make_absolute( points: &Vec<ResultPoint>, left_offset: i32, top_offset: i32) {
|
||||
if points != null {
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < points.len() {
|
||||
{
|
||||
let relative: ResultPoint = points[i];
|
||||
if relative != null {
|
||||
points[i] = ResultPoint::new(relative.get_x() + left_offset, relative.get_y() + top_offset);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MultipleBarcodeReader.java
|
||||
/**
|
||||
* Implementation of this interface attempt to read several barcodes from one image.
|
||||
*
|
||||
* @see com.google.zxing.Reader
|
||||
* @author Sean Owen
|
||||
*/
|
||||
pub trait MultipleBarcodeReader {
|
||||
|
||||
fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
||||
|
||||
fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> ;
|
||||
}
|
||||
|
||||
// GenericMultipleBarcodeReader.java
|
||||
/**
|
||||
* <p>Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image.
|
||||
* After one barcode is found, the areas left, above, right and below the barcode's
|
||||
* {@link ResultPoint}s are scanned, recursively.</p>
|
||||
*
|
||||
* <p>A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple
|
||||
* 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent
|
||||
* detecting any one of them.</p>
|
||||
*
|
||||
* <p>That is, instead of passing a {@link Reader} a caller might pass
|
||||
* {@code new ByQuadrantReader(reader)}.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
const MIN_DIMENSION_TO_RECUR: i32 = 100;
|
||||
|
||||
const MAX_DEPTH: i32 = 4;
|
||||
|
||||
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
||||
|
||||
pub struct GenericMultipleBarcodeReader {
|
||||
|
||||
let delegate: Reader;
|
||||
}
|
||||
|
||||
impl MultipleBarcodeReader for GenericMultipleBarcodeReader {
|
||||
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||
return Ok(self.decode_multiple(image, null));
|
||||
}
|
||||
|
||||
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||
let results: List<Result> = ArrayList<>::new();
|
||||
self.do_decode_multiple(image, &hints, &results, 0, 0, 0);
|
||||
if results.is_empty() {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericMultipleBarcodeReader {
|
||||
|
||||
pub fn new( delegate: &Reader) -> GenericMultipleBarcodeReader {
|
||||
let .delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
fn do_decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>, results: &List<Result>, x_offset: i32, y_offset: i32, current_depth: i32) {
|
||||
if current_depth > MAX_DEPTH {
|
||||
return;
|
||||
}
|
||||
let mut result: Result;
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
result = self.delegate.decode(image, &hints);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( ignored: &ReaderException) {
|
||||
return;
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
let already_found: bool = false;
|
||||
for let existing_result: Result in results {
|
||||
if existing_result.get_text().equals(&result.get_text()) {
|
||||
already_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !already_found {
|
||||
results.add(&::translate_result_points(result, x_offset, y_offset));
|
||||
}
|
||||
let result_points: Vec<ResultPoint> = result.get_result_points();
|
||||
if result_points == null || result_points.len() == 0 {
|
||||
return;
|
||||
}
|
||||
let width: i32 = image.get_width();
|
||||
let height: i32 = image.get_height();
|
||||
let min_x: f32 = width;
|
||||
let min_y: f32 = height;
|
||||
let max_x: f32 = 0.0f;
|
||||
let max_y: f32 = 0.0f;
|
||||
for let point: ResultPoint in result_points {
|
||||
if point == null {
|
||||
continue;
|
||||
}
|
||||
let x: f32 = point.get_x();
|
||||
let y: f32 = point.get_y();
|
||||
if x < min_x {
|
||||
min_x = x;
|
||||
}
|
||||
if y < min_y {
|
||||
min_y = y;
|
||||
}
|
||||
if x > max_x {
|
||||
max_x = x;
|
||||
}
|
||||
if y > max_y {
|
||||
max_y = y;
|
||||
}
|
||||
}
|
||||
// Decode left of barcode
|
||||
if min_x > MIN_DIMENSION_TO_RECUR {
|
||||
self.do_decode_multiple(&image.crop(0, 0, min_x as i32, height), &hints, &results, x_offset, y_offset, current_depth + 1);
|
||||
}
|
||||
// Decode above barcode
|
||||
if min_y > MIN_DIMENSION_TO_RECUR {
|
||||
self.do_decode_multiple(&image.crop(0, 0, width, min_y as i32), &hints, &results, x_offset, y_offset, current_depth + 1);
|
||||
}
|
||||
// Decode right of barcode
|
||||
if max_x < width - MIN_DIMENSION_TO_RECUR {
|
||||
self.do_decode_multiple(&image.crop(max_x as i32, 0, width - max_x as i32, height), &hints, &results, x_offset + max_x as i32, y_offset, current_depth + 1);
|
||||
}
|
||||
// Decode below barcode
|
||||
if max_y < height - MIN_DIMENSION_TO_RECUR {
|
||||
self.do_decode_multiple(&image.crop(0, max_y as i32, width, height - max_y as i32), &hints, &results, x_offset, y_offset + max_y as i32, current_depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_result_points( result: &Result, x_offset: i32, y_offset: i32) -> Result {
|
||||
let old_result_points: Vec<ResultPoint> = result.get_result_points();
|
||||
if old_result_points == null {
|
||||
return result;
|
||||
}
|
||||
let new_result_points: [Option<ResultPoint>; old_result_points.len()] = [None; old_result_points.len()];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < old_result_points.len() {
|
||||
{
|
||||
let old_point: ResultPoint = old_result_points[i];
|
||||
if old_point != null {
|
||||
new_result_points[i] = ResultPoint::new(old_point.get_x() + x_offset, old_point.get_y() + y_offset);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let new_result: Result = Result::new(&result.get_text(), &result.get_raw_bytes(), &result.get_num_bits(), new_result_points, &result.get_barcode_format(), &result.get_timestamp());
|
||||
new_result.put_all_metadata(&result.get_result_metadata());
|
||||
return new_result;
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
use crate::{BarcodeFormat,BinaryBitmap,DecodeHintType,NotFoundException,ReaderException,RXingResult,ResultMetadataType,ResultPoint};
|
||||
use crate::common::{DecoderResult,DetectorResult};
|
||||
use crate::multi::{MultipleBarcodeReader};
|
||||
use crate::multi::qrcode::detector::MultiDetector;
|
||||
use create::qrcode::{QRCodeReader};
|
||||
use crate::multi::qrcode::decoder::QRCodeDecoderMetaData;
|
||||
|
||||
|
||||
// QRCodeMultiReader.java
|
||||
/**
|
||||
* This implementation can detect and decode multiple QR Codes in an image.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
|
||||
const EMPTY_RESULT_ARRAY: [Option<Result>; 0] = [None; 0];
|
||||
|
||||
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
||||
pub struct QRCodeMultiReader {
|
||||
super: QRCodeReader;
|
||||
}
|
||||
|
||||
impl MultipleBarcodeReader for QRCodeMultiReader {
|
||||
pub fn decode_multiple(&self, image: &BinaryBitmap) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||
return Ok(self.decode_multiple(image, null));
|
||||
}
|
||||
|
||||
pub fn decode_multiple(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<Result>, Rc<Exception>> {
|
||||
let mut results: List<Result> = ArrayList<>::new();
|
||||
let detector_results: Vec<DetectorResult> = MultiDetector::new(&image.get_black_matrix()).detect_multi(&hints);
|
||||
for let detector_result: DetectorResult in detector_results {
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let decoder_result: DecoderResult = get_decoder().decode(&detector_result.get_bits(), &hints);
|
||||
let points: Vec<ResultPoint> = detector_result.get_points();
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if decoder_result.get_other() instanceof QRCodeDecoderMetaData {
|
||||
(decoder_result.get_other() as QRCodeDecoderMetaData).apply_mirrored_correction(points);
|
||||
}
|
||||
let result: Result = Result::new(&decoder_result.get_text(), &decoder_result.get_raw_bytes(), points, BarcodeFormat::QR_CODE);
|
||||
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
||||
if byte_segments != null {
|
||||
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
||||
}
|
||||
let ec_level: String = decoder_result.get_e_c_level();
|
||||
if ec_level != null {
|
||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||
}
|
||||
if decoder_result.has_structured_append() {
|
||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE, &decoder_result.get_structured_append_sequence_number());
|
||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_PARITY, &decoder_result.get_structured_append_parity());
|
||||
}
|
||||
results.add(result);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( re: &ReaderException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
}
|
||||
if results.is_empty() {
|
||||
return Ok(EMPTY_RESULT_ARRAY);
|
||||
} else {
|
||||
results = ::process_structured_append(&results);
|
||||
return Ok(results.to_array(EMPTY_RESULT_ARRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QRCodeMultiReader {
|
||||
|
||||
|
||||
|
||||
fn process_structured_append( results: &List<Result>) -> List<Result> {
|
||||
let new_results: List<Result> = ArrayList<>::new();
|
||||
let sa_results: List<Result> = ArrayList<>::new();
|
||||
for let result: Result in results {
|
||||
if result.get_result_metadata().contains_key(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) {
|
||||
sa_results.add(result);
|
||||
} else {
|
||||
new_results.add(result);
|
||||
}
|
||||
}
|
||||
if sa_results.is_empty() {
|
||||
return results;
|
||||
}
|
||||
// sort and concatenate the SA list items
|
||||
Collections::sort(&sa_results, SAComparator::new());
|
||||
let new_text: StringBuilder = StringBuilder::new();
|
||||
let new_raw_bytes: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
||||
let new_byte_segment: ByteArrayOutputStream = ByteArrayOutputStream::new();
|
||||
for let sa_result: Result in sa_results {
|
||||
new_text.append(&sa_result.get_text());
|
||||
let sa_bytes: Vec<i8> = sa_result.get_raw_bytes();
|
||||
new_raw_bytes.write(&sa_bytes, 0, sa_bytes.len());
|
||||
let byte_segments: Iterable<Vec<i8>> = sa_result.get_result_metadata().get(ResultMetadataType::BYTE_SEGMENTS) as Iterable<Vec<i8>>;
|
||||
if byte_segments != null {
|
||||
for let segment: Vec<i8> in byte_segments {
|
||||
new_byte_segment.write(&segment, 0, segment.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
let new_result: Result = Result::new(&new_text.to_string(), &new_raw_bytes.to_byte_array(), NO_POINTS, BarcodeFormat::QR_CODE);
|
||||
if new_byte_segment.size() > 0 {
|
||||
new_result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &Collections::singleton_list(&new_byte_segment.to_byte_array()));
|
||||
}
|
||||
new_results.add(new_result);
|
||||
return new_results;
|
||||
}
|
||||
|
||||
#[derive(Comparator<Result>, Serializable)]
|
||||
struct SAComparator {
|
||||
}
|
||||
|
||||
impl SAComparator {
|
||||
|
||||
pub fn compare(&self, a: &Result, b: &Result) -> i32 {
|
||||
let a_number: i32 = a.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
||||
let b_number: i32 = b.get_result_metadata().get(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE) as i32;
|
||||
return Integer::compare(a_number, b_number);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
use crate::{DecodeHintType,NotFoundException,ReaderException,ResultPointCallback};
|
||||
use crate::common::{BitMatrix,DetectorResult};
|
||||
use crate::qrcode::detector::{Detector,FinderPatternInfo,FinderPattern,FinderPatternFinder};
|
||||
|
||||
// MultiDetector.java
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
|
||||
const EMPTY_DETECTOR_RESULTS: [Option<DetectorResult>; 0] = [None; 0];
|
||||
pub struct MultiDetector {
|
||||
super: Detector;
|
||||
}
|
||||
|
||||
impl Detector for MultiDetector{}
|
||||
|
||||
impl MultiDetector {
|
||||
|
||||
pub fn new( image: &BitMatrix) -> MultiDetector {
|
||||
super(image);
|
||||
}
|
||||
|
||||
pub fn detect_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<DetectorResult>, Rc<Exception>> {
|
||||
let image: BitMatrix = get_image();
|
||||
let result_point_callback: ResultPointCallback = if hints == null { null } else { hints.get(DecodeHintType::NEED_RESULT_POINT_CALLBACK) as ResultPointCallback };
|
||||
let finder: MultiFinderPatternFinder = MultiFinderPatternFinder::new(image, result_point_callback);
|
||||
let infos: Vec<FinderPatternInfo> = finder.find_multi(&hints);
|
||||
if infos.len() == 0 {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
let result: List<DetectorResult> = ArrayList<>::new();
|
||||
for let info: FinderPatternInfo in infos {
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
result.add(&process_finder_pattern_info(info));
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( e: &ReaderException) {
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
}
|
||||
if result.is_empty() {
|
||||
return Ok(EMPTY_DETECTOR_RESULTS);
|
||||
} else {
|
||||
return Ok(result.to_array(EMPTY_DETECTOR_RESULTS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MultiFinderPatternFinder.java
|
||||
/**
|
||||
* <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
||||
* markers at three corners of a QR Code.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
|
||||
*
|
||||
* <p>In contrast to {@link FinderPatternFinder}, this class will return an array of all possible
|
||||
* QR code locations in the image.</p>
|
||||
*
|
||||
* <p>Use the TRY_HARDER hint to ask for a more thorough detection.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Hannes Erven
|
||||
*/
|
||||
|
||||
const EMPTY_RESULT_ARRAY: [Option<FinderPatternInfo>; 0] = [None; 0];
|
||||
|
||||
const EMPTY_FP_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
||||
|
||||
const EMPTY_FP_2D_ARRAY: [Option<FinderPattern>; 0] = [None; 0];
|
||||
|
||||
// TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for
|
||||
// since it limits the number of regions to decode
|
||||
// max. legal count of modules per QR code edge (177)
|
||||
const MAX_MODULE_COUNT_PER_EDGE: f32 = 180;
|
||||
|
||||
// min. legal count per modules per QR code edge (11)
|
||||
const MIN_MODULE_COUNT_PER_EDGE: f32 = 9;
|
||||
|
||||
/**
|
||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their
|
||||
* estimated modules sizes.
|
||||
*/
|
||||
const DIFF_MODSIZE_CUTOFF_PERCENT: f32 = 0.05f;
|
||||
|
||||
/**
|
||||
* More or less arbitrary cutoff point for determining if two finder patterns might belong
|
||||
* to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their
|
||||
* estimated modules sizes.
|
||||
*/
|
||||
const DIFF_MODSIZE_CUTOFF: f32 = 0.5f;
|
||||
pub struct MultiFinderPatternFinder {
|
||||
super: FinderPatternFinder;
|
||||
}
|
||||
|
||||
impl FinderPatternFinder for MultiFinderPatternFinder {}
|
||||
|
||||
impl MultiFinderPatternFinder {
|
||||
|
||||
/**
|
||||
* A comparator that orders FinderPatterns by their estimated module size.
|
||||
*/
|
||||
#[derive(Comparator<FinderPattern>, Serializable)]
|
||||
struct ModuleSizeComparator {
|
||||
}
|
||||
|
||||
impl ModuleSizeComparator {
|
||||
|
||||
pub fn compare(&self, center1: &FinderPattern, center2: &FinderPattern) -> i32 {
|
||||
let value: f32 = center2.get_estimated_module_size() - center1.get_estimated_module_size();
|
||||
return if value < 0.0 { -1 } else { if value > 0.0 { 1 } else { 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn new( image: &BitMatrix, result_point_callback: &ResultPointCallback) -> MultiFinderPatternFinder {
|
||||
super(image, result_point_callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
||||
* those that have been detected at least 2 times, and whose module
|
||||
* size differs from the average among those patterns the least
|
||||
* @throws NotFoundException if 3 such finder patterns do not exist
|
||||
*/
|
||||
fn select_multiple_best_patterns(&self) -> /* throws NotFoundException */Result<Vec<Vec<FinderPattern>>, Rc<Exception>> {
|
||||
let possible_centers: List<FinderPattern> = ArrayList<>::new();
|
||||
for let fp: FinderPattern in get_possible_centers() {
|
||||
if fp.get_count() >= 2 {
|
||||
possible_centers.add(fp);
|
||||
}
|
||||
}
|
||||
let size: i32 = possible_centers.size();
|
||||
if size < 3 {
|
||||
// Couldn't find enough finder patterns
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
/*
|
||||
* Begin HE modifications to safely detect multiple codes of equal size
|
||||
*/
|
||||
if size == 3 {
|
||||
return Ok( : vec![FinderPattern; 1] = vec![possible_centers.to_array(EMPTY_FP_ARRAY), ]
|
||||
);
|
||||
}
|
||||
// Sort by estimated module size to speed up the upcoming checks
|
||||
Collections::sort(&possible_centers, ModuleSizeComparator::new());
|
||||
/*
|
||||
* Now lets start: build a list of tuples of three finder locations that
|
||||
* - feature similar module sizes
|
||||
* - are placed in a distance so the estimated module count is within the QR specification
|
||||
* - have similar distance between upper left/right and left top/bottom finder patterns
|
||||
* - form a triangle with 90° angle (checked by comparing top right/bottom left distance
|
||||
* with pythagoras)
|
||||
*
|
||||
* Note: we allow each point to be used for more than one code region: this might seem
|
||||
* counterintuitive at first, but the performance penalty is not that big. At this point,
|
||||
* we cannot make a good quality decision whether the three finders actually represent
|
||||
* a QR code, or are just by chance laid out so it looks like there might be a QR code there.
|
||||
* So, if the layout seems right, lets have the decoder try to decode.
|
||||
*/
|
||||
// holder for the results
|
||||
let results: List<Vec<FinderPattern>> = ArrayList<>::new();
|
||||
{
|
||||
let mut i1: i32 = 0;
|
||||
while i1 < (size - 2) {
|
||||
{
|
||||
let p1: FinderPattern = possible_centers.get(i1);
|
||||
if p1 == null {
|
||||
continue;
|
||||
}
|
||||
{
|
||||
let mut i2: i32 = i1 + 1;
|
||||
while i2 < (size - 1) {
|
||||
{
|
||||
let p2: FinderPattern = possible_centers.get(i2);
|
||||
if p2 == null {
|
||||
continue;
|
||||
}
|
||||
// Compare the expected module sizes; if they are really off, skip
|
||||
let v_mod_size12: f32 = (p1.get_estimated_module_size() - p2.get_estimated_module_size()) / Math::min(&p1.get_estimated_module_size(), &p2.get_estimated_module_size());
|
||||
let v_mod_size12_a: f32 = Math::abs(p1.get_estimated_module_size() - p2.get_estimated_module_size());
|
||||
if v_mod_size12_a > DIFF_MODSIZE_CUTOFF && v_mod_size12 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
||||
// any more interesting elements for the given p1.
|
||||
break;
|
||||
}
|
||||
{
|
||||
let mut i3: i32 = i2 + 1;
|
||||
while i3 < size {
|
||||
{
|
||||
let p3: FinderPattern = possible_centers.get(i3);
|
||||
if p3 == null {
|
||||
continue;
|
||||
}
|
||||
// Compare the expected module sizes; if they are really off, skip
|
||||
let v_mod_size23: f32 = (p2.get_estimated_module_size() - p3.get_estimated_module_size()) / Math::min(&p2.get_estimated_module_size(), &p3.get_estimated_module_size());
|
||||
let v_mod_size23_a: f32 = Math::abs(p2.get_estimated_module_size() - p3.get_estimated_module_size());
|
||||
if v_mod_size23_a > DIFF_MODSIZE_CUTOFF && v_mod_size23 >= DIFF_MODSIZE_CUTOFF_PERCENT {
|
||||
// any more interesting elements for the given p1.
|
||||
break;
|
||||
}
|
||||
let test: vec![Vec<FinderPattern>; 3] = vec![p1, p2, p3, ]
|
||||
;
|
||||
ResultPoint::order_best_patterns(test);
|
||||
// Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal
|
||||
let info: FinderPatternInfo = FinderPatternInfo::new(test);
|
||||
let d_a: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_bottom_left());
|
||||
let d_c: f32 = ResultPoint::distance(&info.get_top_right(), &info.get_bottom_left());
|
||||
let d_b: f32 = ResultPoint::distance(&info.get_top_left(), &info.get_top_right());
|
||||
// Check the sizes
|
||||
let estimated_module_count: f32 = (d_a + d_b) / (p1.get_estimated_module_size() * 2.0f);
|
||||
if estimated_module_count > MAX_MODULE_COUNT_PER_EDGE || estimated_module_count < MIN_MODULE_COUNT_PER_EDGE {
|
||||
continue;
|
||||
}
|
||||
// Calculate the difference of the edge lengths in percent
|
||||
let v_a_b_b_c: f32 = Math::abs((d_a - d_b) / Math::min(d_a, d_b));
|
||||
if v_a_b_b_c >= 0.1f {
|
||||
continue;
|
||||
}
|
||||
// Calculate the diagonal length by assuming a 90° angle at topleft
|
||||
let d_cpy: f32 = Math::sqrt(d_a as f64 * d_a + d_b as f64 * d_b) as f32;
|
||||
// Compare to the real distance in %
|
||||
let v_py_c: f32 = Math::abs((d_c - d_cpy) / Math::min(d_c, d_cpy));
|
||||
if v_py_c >= 0.1f {
|
||||
continue;
|
||||
}
|
||||
// All tests passed!
|
||||
results.add(test);
|
||||
}
|
||||
i3 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
i2 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
i1 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !results.is_empty() {
|
||||
return Ok(results.to_array(EMPTY_FP_2D_ARRAY));
|
||||
}
|
||||
// Nothing found!
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
|
||||
pub fn find_multi(&self, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Vec<FinderPatternInfo>, Rc<Exception>> {
|
||||
let try_harder: bool = hints != null && hints.contains_key(DecodeHintType::TRY_HARDER);
|
||||
let image: BitMatrix = get_image();
|
||||
let max_i: i32 = image.get_height();
|
||||
let max_j: i32 = image.get_width();
|
||||
// We are looking for black/white/black/white/black modules in
|
||||
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
||||
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||
// QR versions regardless of how dense they are.
|
||||
let i_skip: i32 = (3 * max_i) / (4 * MAX_MODULES);
|
||||
if i_skip < MIN_SKIP || try_harder {
|
||||
i_skip = MIN_SKIP;
|
||||
}
|
||||
let state_count: [i32; 5] = [0; 5];
|
||||
{
|
||||
let mut i: i32 = i_skip - 1;
|
||||
while i < max_i {
|
||||
{
|
||||
// Get a row of black/white values
|
||||
do_clear_counts(&state_count);
|
||||
let current_state: i32 = 0;
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < max_j {
|
||||
{
|
||||
if image.get(j, i) {
|
||||
// Black pixel
|
||||
if (current_state & 1) == 1 {
|
||||
// Counting white pixels
|
||||
current_state += 1;
|
||||
}
|
||||
state_count[current_state] += 1;
|
||||
} else {
|
||||
// White pixel
|
||||
if (current_state & 1) == 0 {
|
||||
// Counting black pixels
|
||||
if current_state == 4 {
|
||||
// A winner?
|
||||
if found_pattern_cross(&state_count) && handle_possible_center(&state_count, i, j) {
|
||||
// Yes
|
||||
// Clear state to start looking again
|
||||
current_state = 0;
|
||||
do_clear_counts(&state_count);
|
||||
} else {
|
||||
// No, shift counts back by two
|
||||
do_shift_counts2(&state_count);
|
||||
current_state = 3;
|
||||
}
|
||||
} else {
|
||||
state_count[current_state += 1] += 1;
|
||||
}
|
||||
} else {
|
||||
// Counting white pixels
|
||||
state_count[current_state] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if found_pattern_cross(&state_count) {
|
||||
handle_possible_center(&state_count, i, max_j);
|
||||
}
|
||||
}
|
||||
i += i_skip;
|
||||
}
|
||||
}
|
||||
|
||||
// for i=iSkip-1 ...
|
||||
let pattern_info: Vec<Vec<FinderPattern>> = self.select_multiple_best_patterns();
|
||||
let result: List<FinderPatternInfo> = ArrayList<>::new();
|
||||
for let pattern: Vec<FinderPattern> in pattern_info {
|
||||
ResultPoint::order_best_patterns(pattern);
|
||||
result.add(FinderPatternInfo::new(pattern));
|
||||
}
|
||||
if result.is_empty() {
|
||||
return Ok(EMPTY_RESULT_ARRAY);
|
||||
} else {
|
||||
return Ok(result.to_array(EMPTY_RESULT_ARRAY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6246
src/oned.rs
6246
src/oned.rs
File diff suppressed because it is too large
Load Diff
959
src/oned/rss.rs
959
src/oned/rss.rs
@@ -1,959 +0,0 @@
|
||||
use crate::{NotFoundException,ResultPoint,BarcodeFormat,DecodeHintType,NotFoundException,RXingResult,ResultMetadataType,ResultPoint,ResultPointCallback};
|
||||
use crate::common::BitArray;
|
||||
use crate::common::detector::{MathUtils};
|
||||
use crate::oned::{OneDReader};
|
||||
|
||||
|
||||
|
||||
// NEW FILE: abstract_r_s_s_reader.rs
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// package com::google::zxing::oned::rss;
|
||||
|
||||
/**
|
||||
* Superclass of {@link OneDReader} implementations that read barcodes in the RSS family
|
||||
* of formats.
|
||||
*/
|
||||
|
||||
const MAX_AVG_VARIANCE: f32 = 0.2f;
|
||||
|
||||
const MAX_INDIVIDUAL_VARIANCE: f32 = 0.45f;
|
||||
|
||||
const MIN_FINDER_PATTERN_RATIO: f32 = 9.5f / 12.0f;
|
||||
|
||||
const MAX_FINDER_PATTERN_RATIO: f32 = 12.5f / 14.0f;
|
||||
pub struct AbstractRSSReader {
|
||||
super: OneDReader;
|
||||
|
||||
let decode_finder_counters: Vec<i32>;
|
||||
|
||||
let data_character_counters: Vec<i32>;
|
||||
|
||||
let odd_rounding_errors: Vec<f32>;
|
||||
|
||||
let even_rounding_errors: Vec<f32>;
|
||||
|
||||
let odd_counts: Vec<i32>;
|
||||
|
||||
let even_counts: Vec<i32>;
|
||||
}
|
||||
|
||||
impl AbstractRSSReader {
|
||||
|
||||
pub fn new() -> AbstractRSSReader {
|
||||
decode_finder_counters = : [i32; 4] = [0; 4];
|
||||
data_character_counters = : [i32; 8] = [0; 8];
|
||||
odd_rounding_errors = : [f32; 4.0] = [0.0; 4.0];
|
||||
even_rounding_errors = : [f32; 4.0] = [0.0; 4.0];
|
||||
odd_counts = : [i32; data_character_counters.len() / 2] = [0; data_character_counters.len() / 2];
|
||||
even_counts = : [i32; data_character_counters.len() / 2] = [0; data_character_counters.len() / 2];
|
||||
}
|
||||
|
||||
pub fn get_decode_finder_counters(&self) -> Vec<i32> {
|
||||
return self.decode_finder_counters;
|
||||
}
|
||||
|
||||
pub fn get_data_character_counters(&self) -> Vec<i32> {
|
||||
return self.data_character_counters;
|
||||
}
|
||||
|
||||
pub fn get_odd_rounding_errors(&self) -> Vec<f32> {
|
||||
return self.odd_rounding_errors;
|
||||
}
|
||||
|
||||
pub fn get_even_rounding_errors(&self) -> Vec<f32> {
|
||||
return self.even_rounding_errors;
|
||||
}
|
||||
|
||||
pub fn get_odd_counts(&self) -> Vec<i32> {
|
||||
return self.odd_counts;
|
||||
}
|
||||
|
||||
pub fn get_even_counts(&self) -> Vec<i32> {
|
||||
return self.even_counts;
|
||||
}
|
||||
|
||||
pub fn parse_finder_value( counters: &Vec<i32>, finder_patterns: &Vec<Vec<i32>>) -> /* throws NotFoundException */Result<i32, Rc<Exception>> {
|
||||
{
|
||||
let mut value: i32 = 0;
|
||||
while value < finder_patterns.len() {
|
||||
{
|
||||
if pattern_match_variance(&counters, finder_patterns[value], MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE {
|
||||
return Ok(value);
|
||||
}
|
||||
}
|
||||
value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array values to sum
|
||||
* @return sum of values
|
||||
* @deprecated call {@link MathUtils#sum(int[])}
|
||||
*/
|
||||
pub fn count( array: &Vec<i32>) -> i32 {
|
||||
return MathUtils::sum(&array);
|
||||
}
|
||||
|
||||
pub fn increment( array: &Vec<i32>, errors: &Vec<f32>) {
|
||||
let mut index: i32 = 0;
|
||||
let biggest_error: f32 = errors[0];
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < array.len() {
|
||||
{
|
||||
if errors[i] > biggest_error {
|
||||
biggest_error = errors[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
array[index] += 1;
|
||||
}
|
||||
|
||||
pub fn decrement( array: &Vec<i32>, errors: &Vec<f32>) {
|
||||
let mut index: i32 = 0;
|
||||
let biggest_error: f32 = errors[0];
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < array.len() {
|
||||
{
|
||||
if errors[i] < biggest_error {
|
||||
biggest_error = errors[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
array[index] -= 1;
|
||||
}
|
||||
|
||||
pub fn is_finder_pattern( counters: &Vec<i32>) -> bool {
|
||||
let first_two_sum: i32 = counters[0] + counters[1];
|
||||
let sum: i32 = first_two_sum + counters[2] + counters[3];
|
||||
let ratio: f32 = first_two_sum / sum as f32;
|
||||
if ratio >= MIN_FINDER_PATTERN_RATIO && ratio <= MAX_FINDER_PATTERN_RATIO {
|
||||
// passes ratio test in spec, but see if the counts are unreasonable
|
||||
let min_counter: i32 = Integer::MAX_VALUE;
|
||||
let max_counter: i32 = Integer::MIN_VALUE;
|
||||
for let counter: i32 in counters {
|
||||
if counter > max_counter {
|
||||
max_counter = counter;
|
||||
}
|
||||
if counter < min_counter {
|
||||
min_counter = counter;
|
||||
}
|
||||
}
|
||||
return max_counter < 10 * min_counter;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: data_character.rs
|
||||
/*
|
||||
* 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::oned::rss;
|
||||
|
||||
/**
|
||||
* Encapsulates a since character value in an RSS barcode, including its checksum information.
|
||||
*/
|
||||
pub struct DataCharacter {
|
||||
|
||||
let value: i32;
|
||||
|
||||
let checksum_portion: i32;
|
||||
}
|
||||
|
||||
impl DataCharacter {
|
||||
|
||||
pub fn new( value: i32, checksum_portion: i32) -> DataCharacter {
|
||||
let .value = value;
|
||||
let .checksumPortion = checksum_portion;
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> i32 {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
pub fn get_checksum_portion(&self) -> i32 {
|
||||
return self.checksum_portion;
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
return format!("{}({})", self.value, self.checksum_portion);
|
||||
}
|
||||
|
||||
pub fn equals(&self, o: &Object) -> bool {
|
||||
if !(o instanceof DataCharacter) {
|
||||
return false;
|
||||
}
|
||||
let that: DataCharacter = o as DataCharacter;
|
||||
return self.value == that.value && self.checksum_portion == that.checksumPortion;
|
||||
}
|
||||
|
||||
pub fn hash_code(&self) -> i32 {
|
||||
return self.value ^ self.checksum_portion;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: finder_pattern.rs
|
||||
/*
|
||||
* 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::oned::rss;
|
||||
|
||||
/**
|
||||
* Encapsulates an RSS barcode finder pattern, including its start/end position and row.
|
||||
*/
|
||||
pub struct FinderPattern {
|
||||
|
||||
let value: i32;
|
||||
|
||||
let start_end: Vec<i32>;
|
||||
|
||||
let result_points: Vec<ResultPoint>;
|
||||
}
|
||||
|
||||
impl FinderPattern {
|
||||
|
||||
pub fn new( value: i32, start_end: &Vec<i32>, start: i32, end: i32, row_number: i32) -> FinderPattern {
|
||||
let .value = value;
|
||||
let .startEnd = start_end;
|
||||
let .resultPoints = : vec![ResultPoint; 2] = vec![ResultPoint::new(start, row_number), ResultPoint::new(end, row_number), ]
|
||||
;
|
||||
}
|
||||
|
||||
pub fn get_value(&self) -> i32 {
|
||||
return self.value;
|
||||
}
|
||||
|
||||
pub fn get_start_end(&self) -> Vec<i32> {
|
||||
return self.start_end;
|
||||
}
|
||||
|
||||
pub fn get_result_points(&self) -> Vec<ResultPoint> {
|
||||
return self.result_points;
|
||||
}
|
||||
|
||||
pub fn equals(&self, o: &Object) -> bool {
|
||||
if !(o instanceof FinderPattern) {
|
||||
return false;
|
||||
}
|
||||
let that: FinderPattern = o as FinderPattern;
|
||||
return self.value == that.value;
|
||||
}
|
||||
|
||||
pub fn hash_code(&self) -> i32 {
|
||||
return self.value;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: pair.rs
|
||||
/*
|
||||
* 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::oned::rss;
|
||||
|
||||
struct Pair {
|
||||
super: DataCharacter;
|
||||
|
||||
let finder_pattern: FinderPattern;
|
||||
|
||||
let mut count: i32;
|
||||
}
|
||||
|
||||
impl Pair {
|
||||
|
||||
fn new( value: i32, checksum_portion: i32, finder_pattern: &FinderPattern) -> Pair {
|
||||
super(value, checksum_portion);
|
||||
let .finderPattern = finder_pattern;
|
||||
}
|
||||
|
||||
fn get_finder_pattern(&self) -> FinderPattern {
|
||||
return self.finder_pattern;
|
||||
}
|
||||
|
||||
fn get_count(&self) -> i32 {
|
||||
return self.count;
|
||||
}
|
||||
|
||||
fn increment_count(&self) {
|
||||
self.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: r_s_s14_reader.rs
|
||||
/*
|
||||
* 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::oned::rss;
|
||||
|
||||
/**
|
||||
* Decodes RSS-14, including truncated and stacked variants. See ISO/IEC 24724:2006.
|
||||
*/
|
||||
|
||||
const OUTSIDE_EVEN_TOTAL_SUBSET: vec![Vec<i32>; 5] = vec![1, 10, 34, 70, 126, ]
|
||||
;
|
||||
|
||||
const INSIDE_ODD_TOTAL_SUBSET: vec![Vec<i32>; 4] = vec![4, 20, 48, 81, ]
|
||||
;
|
||||
|
||||
const OUTSIDE_GSUM: vec![Vec<i32>; 5] = vec![0, 161, 961, 2015, 2715, ]
|
||||
;
|
||||
|
||||
const INSIDE_GSUM: vec![Vec<i32>; 4] = vec![0, 336, 1036, 1516, ]
|
||||
;
|
||||
|
||||
const OUTSIDE_ODD_WIDEST: vec![Vec<i32>; 5] = vec![8, 6, 4, 3, 1, ]
|
||||
;
|
||||
|
||||
const INSIDE_ODD_WIDEST: vec![Vec<i32>; 4] = vec![2, 4, 6, 8, ]
|
||||
;
|
||||
|
||||
const FINDER_PATTERNS: vec![vec![Vec<Vec<i32>>; 4]; 9] = vec![vec![3, 8, 2, 1, ]
|
||||
, vec![3, 5, 5, 1, ]
|
||||
, vec![3, 3, 7, 1, ]
|
||||
, vec![3, 1, 9, 1, ]
|
||||
, vec![2, 7, 4, 1, ]
|
||||
, vec![2, 5, 6, 1, ]
|
||||
, vec![2, 3, 8, 1, ]
|
||||
, vec![1, 5, 7, 1, ]
|
||||
, vec![1, 3, 9, 1, ]
|
||||
, ]
|
||||
;
|
||||
pub struct RSS14Reader {
|
||||
super: AbstractRSSReader;
|
||||
|
||||
let possible_left_pairs: List<Pair>;
|
||||
|
||||
let possible_right_pairs: List<Pair>;
|
||||
}
|
||||
|
||||
impl RSS14Reader {
|
||||
|
||||
pub fn new() -> RSS14Reader {
|
||||
possible_left_pairs = ArrayList<>::new();
|
||||
possible_right_pairs = ArrayList<>::new();
|
||||
}
|
||||
|
||||
pub fn decode_row(&self, row_number: i32, row: &BitArray, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException */Result<Result, Rc<Exception>> {
|
||||
let left_pair: Pair = self.decode_pair(row, false, row_number, &hints);
|
||||
::add_or_tally(&self.possible_left_pairs, left_pair);
|
||||
row.reverse();
|
||||
let right_pair: Pair = self.decode_pair(row, true, row_number, &hints);
|
||||
::add_or_tally(&self.possible_right_pairs, right_pair);
|
||||
row.reverse();
|
||||
for let left: Pair in self.possible_left_pairs {
|
||||
if left.get_count() > 1 {
|
||||
for let right: Pair in self.possible_right_pairs {
|
||||
if right.get_count() > 1 && ::check_checksum(left, right) {
|
||||
return Ok(::construct_result(left, right));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
|
||||
fn add_or_tally( possible_pairs: &Collection<Pair>, pair: &Pair) {
|
||||
if pair == null {
|
||||
return;
|
||||
}
|
||||
let mut found: bool = false;
|
||||
for let other: Pair in possible_pairs {
|
||||
if other.get_value() == pair.get_value() {
|
||||
other.increment_count();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
possible_pairs.add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
self.possible_left_pairs.clear();
|
||||
self.possible_right_pairs.clear();
|
||||
}
|
||||
|
||||
fn construct_result( left_pair: &Pair, right_pair: &Pair) -> Result {
|
||||
let symbol_value: i64 = 4537077 * left_pair.get_value() + right_pair.get_value();
|
||||
let text: String = String::value_of(symbol_value);
|
||||
let buffer: StringBuilder = StringBuilder::new(14);
|
||||
{
|
||||
let mut i: i32 = 13 - text.length();
|
||||
while i > 0 {
|
||||
{
|
||||
buffer.append('0');
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.append(&text);
|
||||
let check_digit: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < 13 {
|
||||
{
|
||||
let digit: i32 = buffer.char_at(i) - '0';
|
||||
check_digit += if (i & 0x01) == 0 { 3 * digit } else { digit };
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
check_digit = 10 - (check_digit % 10);
|
||||
if check_digit == 10 {
|
||||
check_digit = 0;
|
||||
}
|
||||
buffer.append(check_digit);
|
||||
let left_points: Vec<ResultPoint> = left_pair.get_finder_pattern().get_result_points();
|
||||
let right_points: Vec<ResultPoint> = right_pair.get_finder_pattern().get_result_points();
|
||||
let result: Result = Result::new(&buffer.to_string(), null, : vec![ResultPoint; 4] = vec![left_points[0], left_points[1], right_points[0], right_points[1], ]
|
||||
, BarcodeFormat::RSS_14);
|
||||
result.put_metadata(ResultMetadataType::SYMBOLOGY_IDENTIFIER, "]e0");
|
||||
return result;
|
||||
}
|
||||
|
||||
fn check_checksum( left_pair: &Pair, right_pair: &Pair) -> bool {
|
||||
let check_value: i32 = (left_pair.get_checksum_portion() + 16 * right_pair.get_checksum_portion()) % 79;
|
||||
let target_check_value: i32 = 9 * left_pair.get_finder_pattern().get_value() + right_pair.get_finder_pattern().get_value();
|
||||
if target_check_value > 72 {
|
||||
target_check_value -= 1;
|
||||
}
|
||||
if target_check_value > 8 {
|
||||
target_check_value -= 1;
|
||||
}
|
||||
return check_value == target_check_value;
|
||||
}
|
||||
|
||||
fn decode_pair(&self, row: &BitArray, right: bool, row_number: i32, hints: &Map<DecodeHintType, ?>) -> Pair {
|
||||
let tryResult1 = 0;
|
||||
'try1: loop {
|
||||
{
|
||||
let start_end: Vec<i32> = self.find_finder_pattern(row, right);
|
||||
let pattern: FinderPattern = self.parse_found_finder_pattern(row, row_number, right, &start_end);
|
||||
let result_point_callback: ResultPointCallback = if hints == null { null } else { hints.get(DecodeHintType::NEED_RESULT_POINT_CALLBACK) as ResultPointCallback };
|
||||
if result_point_callback != null {
|
||||
start_end = pattern.get_start_end();
|
||||
let mut center: f32 = (start_end[0] + start_end[1] - 1.0) / 2.0f;
|
||||
if right {
|
||||
// row is actually reversed
|
||||
center = row.get_size() - 1.0 - center;
|
||||
}
|
||||
result_point_callback.found_possible_result_point(ResultPoint::new(center, row_number));
|
||||
}
|
||||
let outside: DataCharacter = self.decode_data_character(row, pattern, true);
|
||||
let inside: DataCharacter = self.decode_data_character(row, pattern, false);
|
||||
return Pair::new(1597 * outside.get_value() + inside.get_value(), outside.get_checksum_portion() + 4 * inside.get_checksum_portion(), pattern);
|
||||
}
|
||||
break 'try1
|
||||
}
|
||||
match tryResult1 {
|
||||
catch ( ignored: &NotFoundException) {
|
||||
return null;
|
||||
} 0 => break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn decode_data_character(&self, row: &BitArray, pattern: &FinderPattern, outside_char: bool) -> /* throws NotFoundException */Result<DataCharacter, Rc<Exception>> {
|
||||
let mut counters: Vec<i32> = get_data_character_counters();
|
||||
Arrays::fill(&counters, 0);
|
||||
if outside_char {
|
||||
record_pattern_in_reverse(row, pattern.get_start_end()[0], &counters);
|
||||
} else {
|
||||
record_pattern(row, pattern.get_start_end()[1], &counters);
|
||||
// reverse it
|
||||
{
|
||||
let mut i: i32 = 0, let mut j: i32 = counters.len() - 1;
|
||||
while i < j {
|
||||
{
|
||||
let temp: i32 = counters[i];
|
||||
counters[i] = counters[j];
|
||||
counters[j] = temp;
|
||||
}
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let num_modules: i32 = if outside_char { 16 } else { 15 };
|
||||
let element_width: f32 = MathUtils::sum(&counters) / num_modules as f32;
|
||||
let odd_counts: Vec<i32> = self.get_odd_counts();
|
||||
let even_counts: Vec<i32> = self.get_even_counts();
|
||||
let odd_rounding_errors: Vec<f32> = self.get_odd_rounding_errors();
|
||||
let even_rounding_errors: Vec<f32> = self.get_even_rounding_errors();
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < counters.len() {
|
||||
{
|
||||
let value: f32 = counters[i] / element_width;
|
||||
// Round
|
||||
let mut count: i32 = (value + 0.5f) as i32;
|
||||
if count < 1 {
|
||||
count = 1;
|
||||
} else if count > 8 {
|
||||
count = 8;
|
||||
}
|
||||
let mut offset: i32 = i / 2;
|
||||
if (i & 0x01) == 0 {
|
||||
odd_counts[offset] = count;
|
||||
odd_rounding_errors[offset] = value - count;
|
||||
} else {
|
||||
even_counts[offset] = count;
|
||||
even_rounding_errors[offset] = value - count;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.adjust_odd_even_counts(outside_char, num_modules);
|
||||
let odd_sum: i32 = 0;
|
||||
let odd_checksum_portion: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = odd_counts.len() - 1;
|
||||
while i >= 0 {
|
||||
{
|
||||
odd_checksum_portion *= 9;
|
||||
odd_checksum_portion += odd_counts[i];
|
||||
odd_sum += odd_counts[i];
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let even_checksum_portion: i32 = 0;
|
||||
let even_sum: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = even_counts.len() - 1;
|
||||
while i >= 0 {
|
||||
{
|
||||
even_checksum_portion *= 9;
|
||||
even_checksum_portion += even_counts[i];
|
||||
even_sum += even_counts[i];
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let checksum_portion: i32 = odd_checksum_portion + 3 * even_checksum_portion;
|
||||
if outside_char {
|
||||
if (odd_sum & 0x01) != 0 || odd_sum > 12 || odd_sum < 4 {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
let group: i32 = (12 - odd_sum) / 2;
|
||||
let odd_widest: i32 = OUTSIDE_ODD_WIDEST[group];
|
||||
let even_widest: i32 = 9 - odd_widest;
|
||||
let v_odd: i32 = RSSUtils::get_r_s_svalue(&odd_counts, odd_widest, false);
|
||||
let v_even: i32 = RSSUtils::get_r_s_svalue(&even_counts, even_widest, true);
|
||||
let t_even: i32 = OUTSIDE_EVEN_TOTAL_SUBSET[group];
|
||||
let g_sum: i32 = OUTSIDE_GSUM[group];
|
||||
return Ok(DataCharacter::new(v_odd * t_even + v_even + g_sum, checksum_portion));
|
||||
} else {
|
||||
if (even_sum & 0x01) != 0 || even_sum > 10 || even_sum < 4 {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
let group: i32 = (10 - even_sum) / 2;
|
||||
let odd_widest: i32 = INSIDE_ODD_WIDEST[group];
|
||||
let even_widest: i32 = 9 - odd_widest;
|
||||
let v_odd: i32 = RSSUtils::get_r_s_svalue(&odd_counts, odd_widest, true);
|
||||
let v_even: i32 = RSSUtils::get_r_s_svalue(&even_counts, even_widest, false);
|
||||
let t_odd: i32 = INSIDE_ODD_TOTAL_SUBSET[group];
|
||||
let g_sum: i32 = INSIDE_GSUM[group];
|
||||
return Ok(DataCharacter::new(v_even * t_odd + v_odd + g_sum, checksum_portion));
|
||||
}
|
||||
}
|
||||
|
||||
fn find_finder_pattern(&self, row: &BitArray, right_finder_pattern: bool) -> /* throws NotFoundException */Result<Vec<i32>, Rc<Exception>> {
|
||||
let mut counters: Vec<i32> = get_decode_finder_counters();
|
||||
counters[0] = 0;
|
||||
counters[1] = 0;
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
let width: i32 = row.get_size();
|
||||
let is_white: bool = false;
|
||||
let row_offset: i32 = 0;
|
||||
while row_offset < width {
|
||||
is_white = !row.get(row_offset);
|
||||
if right_finder_pattern == is_white {
|
||||
// Will encounter white first when searching for right finder pattern
|
||||
break;
|
||||
}
|
||||
row_offset += 1;
|
||||
}
|
||||
let counter_position: i32 = 0;
|
||||
let pattern_start: i32 = row_offset;
|
||||
{
|
||||
let mut x: i32 = row_offset;
|
||||
while x < width {
|
||||
{
|
||||
if row.get(x) != is_white {
|
||||
counters[counter_position] += 1;
|
||||
} else {
|
||||
if counter_position == 3 {
|
||||
if is_finder_pattern(&counters) {
|
||||
return Ok( : vec![i32; 2] = vec![pattern_start, x, ]
|
||||
);
|
||||
}
|
||||
pattern_start += counters[0] + counters[1];
|
||||
counters[0] = counters[2];
|
||||
counters[1] = counters[3];
|
||||
counters[2] = 0;
|
||||
counters[3] = 0;
|
||||
counter_position -= 1;
|
||||
} else {
|
||||
counter_position += 1;
|
||||
}
|
||||
counters[counter_position] = 1;
|
||||
is_white = !is_white;
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
|
||||
fn parse_found_finder_pattern(&self, row: &BitArray, row_number: i32, right: bool, start_end: &Vec<i32>) -> /* throws NotFoundException */Result<FinderPattern, Rc<Exception>> {
|
||||
// Actually we found elements 2-5
|
||||
let first_is_black: bool = row.get(start_end[0]);
|
||||
let first_element_start: i32 = start_end[0] - 1;
|
||||
// Locate element 1
|
||||
while first_element_start >= 0 && first_is_black != row.get(first_element_start) {
|
||||
first_element_start -= 1;
|
||||
}
|
||||
first_element_start += 1;
|
||||
let first_counter: i32 = start_end[0] - first_element_start;
|
||||
// Make 'counters' hold 1-4
|
||||
let mut counters: Vec<i32> = get_decode_finder_counters();
|
||||
System::arraycopy(&counters, 0, &counters, 1, counters.len() - 1);
|
||||
counters[0] = first_counter;
|
||||
let value: i32 = parse_finder_value(&counters, &FINDER_PATTERNS);
|
||||
let mut start: i32 = first_element_start;
|
||||
let mut end: i32 = start_end[1];
|
||||
if right {
|
||||
// row is actually reversed
|
||||
start = row.get_size() - 1 - start;
|
||||
end = row.get_size() - 1 - end;
|
||||
}
|
||||
return Ok(FinderPattern::new(value, : vec![i32; 2] = vec![first_element_start, start_end[1], ]
|
||||
, start, end, row_number));
|
||||
}
|
||||
|
||||
fn adjust_odd_even_counts(&self, outside_char: bool, num_modules: i32) -> /* throws NotFoundException */Result<Void, Rc<Exception>> {
|
||||
let odd_sum: i32 = MathUtils::sum(&get_odd_counts());
|
||||
let even_sum: i32 = MathUtils::sum(&get_even_counts());
|
||||
let increment_odd: bool = false;
|
||||
let decrement_odd: bool = false;
|
||||
let increment_even: bool = false;
|
||||
let decrement_even: bool = false;
|
||||
if outside_char {
|
||||
if odd_sum > 12 {
|
||||
decrement_odd = true;
|
||||
} else if odd_sum < 4 {
|
||||
increment_odd = true;
|
||||
}
|
||||
if even_sum > 12 {
|
||||
decrement_even = true;
|
||||
} else if even_sum < 4 {
|
||||
increment_even = true;
|
||||
}
|
||||
} else {
|
||||
if odd_sum > 11 {
|
||||
decrement_odd = true;
|
||||
} else if odd_sum < 5 {
|
||||
increment_odd = true;
|
||||
}
|
||||
if even_sum > 10 {
|
||||
decrement_even = true;
|
||||
} else if even_sum < 4 {
|
||||
increment_even = true;
|
||||
}
|
||||
}
|
||||
let mismatch: i32 = odd_sum + even_sum - num_modules;
|
||||
let odd_parity_bad: bool = (odd_sum & 0x01) == ( if outside_char { 1 } else { 0 });
|
||||
let even_parity_bad: bool = (even_sum & 0x01) == 1;
|
||||
/*if (mismatch == 2) {
|
||||
if (!(oddParityBad && evenParityBad)) {
|
||||
throw ReaderException.getInstance();
|
||||
}
|
||||
decrementOdd = true;
|
||||
decrementEven = true;
|
||||
} else if (mismatch == -2) {
|
||||
if (!(oddParityBad && evenParityBad)) {
|
||||
throw ReaderException.getInstance();
|
||||
}
|
||||
incrementOdd = true;
|
||||
incrementEven = true;
|
||||
} else */
|
||||
match mismatch {
|
||||
1 =>
|
||||
{
|
||||
if odd_parity_bad {
|
||||
if even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
decrement_odd = true;
|
||||
} else {
|
||||
if !even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
decrement_even = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
-1 =>
|
||||
{
|
||||
if odd_parity_bad {
|
||||
if even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
increment_odd = true;
|
||||
} else {
|
||||
if !even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
increment_even = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
0 =>
|
||||
{
|
||||
if odd_parity_bad {
|
||||
if !even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
// Both bad
|
||||
if odd_sum < even_sum {
|
||||
increment_odd = true;
|
||||
decrement_even = true;
|
||||
} else {
|
||||
decrement_odd = true;
|
||||
increment_even = true;
|
||||
}
|
||||
} else {
|
||||
if even_parity_bad {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
// Nothing to do!
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
}
|
||||
if increment_odd {
|
||||
if decrement_odd {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
increment(&get_odd_counts(), &get_odd_rounding_errors());
|
||||
}
|
||||
if decrement_odd {
|
||||
decrement(&get_odd_counts(), &get_odd_rounding_errors());
|
||||
}
|
||||
if increment_even {
|
||||
if decrement_even {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
increment(&get_even_counts(), &get_odd_rounding_errors());
|
||||
}
|
||||
if decrement_even {
|
||||
decrement(&get_even_counts(), &get_even_rounding_errors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: r_s_s_utils.rs
|
||||
/*
|
||||
* 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::oned::rss;
|
||||
|
||||
/** Adapted from listings in ISO/IEC 24724 Appendix B and Appendix G. */
|
||||
pub struct RSSUtils {
|
||||
}
|
||||
|
||||
impl RSSUtils {
|
||||
|
||||
fn new() -> RSSUtils {
|
||||
}
|
||||
|
||||
pub fn get_r_s_svalue( widths: &Vec<i32>, max_width: i32, no_narrow: bool) -> i32 {
|
||||
let mut n: i32 = 0;
|
||||
for let width: i32 in widths {
|
||||
n += width;
|
||||
}
|
||||
let mut val: i32 = 0;
|
||||
let narrow_mask: i32 = 0;
|
||||
let elements: i32 = widths.len();
|
||||
{
|
||||
let mut bar: i32 = 0;
|
||||
while bar < elements - 1 {
|
||||
{
|
||||
let elm_width: i32;
|
||||
{
|
||||
elm_width = 1;
|
||||
narrow_mask |= 1 << bar;
|
||||
while elm_width < widths[bar] {
|
||||
{
|
||||
let sub_val: i32 = ::combins(n - elm_width - 1, elements - bar - 2);
|
||||
if no_narrow && (narrow_mask == 0) && (n - elm_width - (elements - bar - 1) >= elements - bar - 1) {
|
||||
sub_val -= ::combins(n - elm_width - (elements - bar), elements - bar - 2);
|
||||
}
|
||||
if elements - bar - 1 > 1 {
|
||||
let less_val: i32 = 0;
|
||||
{
|
||||
let mxw_element: i32 = n - elm_width - (elements - bar - 2);
|
||||
while mxw_element > max_width {
|
||||
{
|
||||
less_val += ::combins(n - elm_width - mxw_element - 1, elements - bar - 3);
|
||||
}
|
||||
mxw_element -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub_val -= less_val * (elements - 1 - bar);
|
||||
} else if n - elm_width > max_width {
|
||||
sub_val -= 1;
|
||||
}
|
||||
val += sub_val;
|
||||
}
|
||||
elm_width += 1;
|
||||
narrow_mask &= ~(1 << bar);
|
||||
}
|
||||
}
|
||||
|
||||
n -= elm_width;
|
||||
}
|
||||
bar += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
fn combins( n: i32, r: i32) -> i32 {
|
||||
let max_denom: i32;
|
||||
let min_denom: i32;
|
||||
if n - r > r {
|
||||
min_denom = r;
|
||||
max_denom = n - r;
|
||||
} else {
|
||||
min_denom = n - r;
|
||||
max_denom = r;
|
||||
}
|
||||
let mut val: i32 = 1;
|
||||
let mut j: i32 = 1;
|
||||
{
|
||||
let mut i: i32 = n;
|
||||
while i > max_denom {
|
||||
{
|
||||
val *= i;
|
||||
if j <= min_denom {
|
||||
val /= j;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
while j <= min_denom {
|
||||
val /= j;
|
||||
j += 1;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
570
src/pdf417.rs
570
src/pdf417.rs
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,630 +0,0 @@
|
||||
use crate::ChecksumException;
|
||||
use crate::pdf417::PDF417Common;
|
||||
|
||||
|
||||
|
||||
// NEW FILE: error_correction.rs
|
||||
/*
|
||||
* Copyright 2012 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::pdf417::decoder::ec;
|
||||
|
||||
/**
|
||||
* <p>PDF417 error correction implementation.</p>
|
||||
*
|
||||
* <p>This <a href="http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction#Example">example</a>
|
||||
* is quite useful in understanding the algorithm.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.ReedSolomonDecoder
|
||||
*/
|
||||
pub struct ErrorCorrection {
|
||||
|
||||
let mut field: ModulusGF;
|
||||
}
|
||||
|
||||
impl ErrorCorrection {
|
||||
|
||||
pub fn new() -> ErrorCorrection {
|
||||
let .field = ModulusGF::PDF417_GF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param received received codewords
|
||||
* @param numECCodewords number of those codewords used for EC
|
||||
* @param erasures location of erasures
|
||||
* @return number of errors
|
||||
* @throws ChecksumException if errors cannot be corrected, maybe because of too many errors
|
||||
*/
|
||||
pub fn decode(&self, received: &Vec<i32>, num_e_c_codewords: i32, erasures: &Vec<i32>) -> /* throws ChecksumException */Result<i32, Rc<Exception>> {
|
||||
let poly: ModulusPoly = ModulusPoly::new(self.field, &received);
|
||||
const S: [i32; num_e_c_codewords] = [0; num_e_c_codewords];
|
||||
let mut error: bool = false;
|
||||
{
|
||||
let mut i: i32 = num_e_c_codewords;
|
||||
while i > 0 {
|
||||
{
|
||||
let eval: i32 = poly.evaluate_at(&self.field.exp(i));
|
||||
S[num_e_c_codewords - i] = eval;
|
||||
if eval != 0 {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !error {
|
||||
return Ok(0);
|
||||
}
|
||||
let known_errors: ModulusPoly = self.field.get_one();
|
||||
if erasures != null {
|
||||
for let erasure: i32 in erasures {
|
||||
let b: i32 = self.field.exp(received.len() - 1 - erasure);
|
||||
// Add (1 - bx) term:
|
||||
let term: ModulusPoly = ModulusPoly::new(self.field, : vec![i32; 2] = vec![self.field.subtract(0, b), 1, ]
|
||||
);
|
||||
known_errors = known_errors.multiply(term);
|
||||
}
|
||||
}
|
||||
let syndrome: ModulusPoly = ModulusPoly::new(self.field, &S);
|
||||
//syndrome = syndrome.multiply(knownErrors);
|
||||
let sigma_omega: Vec<ModulusPoly> = self.run_euclidean_algorithm(&self.field.build_monomial(num_e_c_codewords, 1), syndrome, num_e_c_codewords);
|
||||
let sigma: ModulusPoly = sigma_omega[0];
|
||||
let omega: ModulusPoly = sigma_omega[1];
|
||||
//sigma = sigma.multiply(knownErrors);
|
||||
let error_locations: Vec<i32> = self.find_error_locations(sigma);
|
||||
let error_magnitudes: Vec<i32> = self.find_error_magnitudes(omega, sigma, &error_locations);
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < error_locations.len() {
|
||||
{
|
||||
let mut position: i32 = received.len() - 1 - self.field.log(error_locations[i]);
|
||||
if position < 0 {
|
||||
throw ChecksumException::get_checksum_instance();
|
||||
}
|
||||
received[position] = self.field.subtract(received[position], error_magnitudes[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(error_locations.len());
|
||||
}
|
||||
|
||||
fn run_euclidean_algorithm(&self, a: &ModulusPoly, b: &ModulusPoly, R: i32) -> /* throws ChecksumException */Result<Vec<ModulusPoly>, Rc<Exception>> {
|
||||
// Assume a's degree is >= b's
|
||||
if a.get_degree() < b.get_degree() {
|
||||
let temp: ModulusPoly = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
let r_last: ModulusPoly = a;
|
||||
let mut r: ModulusPoly = b;
|
||||
let t_last: ModulusPoly = self.field.get_zero();
|
||||
let mut t: ModulusPoly = self.field.get_one();
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while r.get_degree() >= R / 2 {
|
||||
let r_last_last: ModulusPoly = r_last;
|
||||
let t_last_last: ModulusPoly = t_last;
|
||||
r_last = r;
|
||||
t_last = t;
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if r_last.is_zero() {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
throw ChecksumException::get_checksum_instance();
|
||||
}
|
||||
r = r_last_last;
|
||||
let mut q: ModulusPoly = self.field.get_zero();
|
||||
let denominator_leading_term: i32 = r_last.get_coefficient(&r_last.get_degree());
|
||||
let dlt_inverse: i32 = self.field.inverse(denominator_leading_term);
|
||||
while r.get_degree() >= r_last.get_degree() && !r.is_zero() {
|
||||
let degree_diff: i32 = r.get_degree() - r_last.get_degree();
|
||||
let scale: i32 = self.field.multiply(&r.get_coefficient(&r.get_degree()), dlt_inverse);
|
||||
q = q.add(&self.field.build_monomial(degree_diff, scale));
|
||||
r = r.subtract(&r_last.multiply_by_monomial(degree_diff, scale));
|
||||
}
|
||||
t = q.multiply(t_last).subtract(t_last_last).negative();
|
||||
}
|
||||
let sigma_tilde_at_zero: i32 = t.get_coefficient(0);
|
||||
if sigma_tilde_at_zero == 0 {
|
||||
throw ChecksumException::get_checksum_instance();
|
||||
}
|
||||
let inverse: i32 = self.field.inverse(sigma_tilde_at_zero);
|
||||
let sigma: ModulusPoly = t.multiply(inverse);
|
||||
let omega: ModulusPoly = r.multiply(inverse);
|
||||
return Ok( : vec![ModulusPoly; 2] = vec![sigma, omega, ]
|
||||
);
|
||||
}
|
||||
|
||||
fn find_error_locations(&self, error_locator: &ModulusPoly) -> /* throws ChecksumException */Result<Vec<i32>, Rc<Exception>> {
|
||||
// This is a direct application of Chien's search
|
||||
let num_errors: i32 = error_locator.get_degree();
|
||||
let mut result: [i32; num_errors] = [0; num_errors];
|
||||
let mut e: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < self.field.get_size() && e < num_errors {
|
||||
{
|
||||
if error_locator.evaluate_at(i) == 0 {
|
||||
result[e] = self.field.inverse(i);
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if e != num_errors {
|
||||
throw ChecksumException::get_checksum_instance();
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
fn find_error_magnitudes(&self, error_evaluator: &ModulusPoly, error_locator: &ModulusPoly, error_locations: &Vec<i32>) -> Vec<i32> {
|
||||
let error_locator_degree: i32 = error_locator.get_degree();
|
||||
if error_locator_degree < 1 {
|
||||
return : [i32; 0] = [0; 0];
|
||||
}
|
||||
let formal_derivative_coefficients: [i32; error_locator_degree] = [0; error_locator_degree];
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i <= error_locator_degree {
|
||||
{
|
||||
formal_derivative_coefficients[error_locator_degree - i] = self.field.multiply(i, &error_locator.get_coefficient(i));
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let formal_derivative: ModulusPoly = ModulusPoly::new(self.field, &formal_derivative_coefficients);
|
||||
// This is directly applying Forney's Formula
|
||||
let s: i32 = error_locations.len();
|
||||
let mut result: [i32; s] = [0; s];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < s {
|
||||
{
|
||||
let xi_inverse: i32 = self.field.inverse(error_locations[i]);
|
||||
let numerator: i32 = self.field.subtract(0, &error_evaluator.evaluate_at(xi_inverse));
|
||||
let denominator: i32 = self.field.inverse(&formal_derivative.evaluate_at(xi_inverse));
|
||||
result[i] = self.field.multiply(numerator, denominator);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: modulus_g_f.rs
|
||||
/*
|
||||
* Copyright 2012 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::pdf417::decoder::ec;
|
||||
|
||||
/**
|
||||
* <p>A field based on powers of a generator integer, modulo some modulus.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see com.google.zxing.common.reedsolomon.GenericGF
|
||||
*/
|
||||
|
||||
const PDF417_GF: ModulusGF = ModulusGF::new(PDF417Common.NUMBER_OF_CODEWORDS, 3);
|
||||
pub struct ModulusGF {
|
||||
|
||||
let exp_table: Vec<i32>;
|
||||
|
||||
let log_table: Vec<i32>;
|
||||
|
||||
let mut zero: ModulusPoly;
|
||||
|
||||
let mut one: ModulusPoly;
|
||||
|
||||
let modulus: i32;
|
||||
}
|
||||
|
||||
impl ModulusGF {
|
||||
|
||||
fn new( modulus: i32, generator: i32) -> ModulusGF {
|
||||
let .modulus = modulus;
|
||||
exp_table = : [i32; modulus] = [0; modulus];
|
||||
log_table = : [i32; modulus] = [0; modulus];
|
||||
let mut x: i32 = 1;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < modulus {
|
||||
{
|
||||
exp_table[i] = x;
|
||||
x = (x * generator) % modulus;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < modulus - 1 {
|
||||
{
|
||||
log_table[exp_table[i]] = i;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// logTable[0] == 0 but this should never be used
|
||||
zero = ModulusPoly::new(let , : vec![i32; 1] = vec![0, ]
|
||||
);
|
||||
one = ModulusPoly::new(let , : vec![i32; 1] = vec![1, ]
|
||||
);
|
||||
}
|
||||
|
||||
fn get_zero(&self) -> ModulusPoly {
|
||||
return self.zero;
|
||||
}
|
||||
|
||||
fn get_one(&self) -> ModulusPoly {
|
||||
return self.one;
|
||||
}
|
||||
|
||||
fn build_monomial(&self, degree: i32, coefficient: i32) -> ModulusPoly {
|
||||
if degree < 0 {
|
||||
throw IllegalArgumentException::new();
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return self.zero;
|
||||
}
|
||||
let mut coefficients: [i32; degree + 1] = [0; degree + 1];
|
||||
coefficients[0] = coefficient;
|
||||
return ModulusPoly::new(self, &coefficients);
|
||||
}
|
||||
|
||||
fn add(&self, a: i32, b: i32) -> i32 {
|
||||
return (a + b) % self.modulus;
|
||||
}
|
||||
|
||||
fn subtract(&self, a: i32, b: i32) -> i32 {
|
||||
return (self.modulus + a - b) % self.modulus;
|
||||
}
|
||||
|
||||
fn exp(&self, a: i32) -> i32 {
|
||||
return self.exp_table[a];
|
||||
}
|
||||
|
||||
fn log(&self, a: i32) -> i32 {
|
||||
if a == 0 {
|
||||
throw IllegalArgumentException::new();
|
||||
}
|
||||
return self.log_table[a];
|
||||
}
|
||||
|
||||
fn inverse(&self, a: i32) -> i32 {
|
||||
if a == 0 {
|
||||
throw ArithmeticException::new();
|
||||
}
|
||||
return self.exp_table[self.modulus - self.log_table[a] - 1];
|
||||
}
|
||||
|
||||
fn multiply(&self, a: i32, b: i32) -> i32 {
|
||||
if a == 0 || b == 0 {
|
||||
return 0;
|
||||
}
|
||||
return self.exp_table[(self.log_table[a] + self.log_table[b]) % (self.modulus - 1)];
|
||||
}
|
||||
|
||||
fn get_size(&self) -> i32 {
|
||||
return self.modulus;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: modulus_poly.rs
|
||||
/*
|
||||
* Copyright 2012 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::pdf417::decoder::ec;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
struct ModulusPoly {
|
||||
|
||||
let field: ModulusGF;
|
||||
|
||||
let coefficients: Vec<i32>;
|
||||
}
|
||||
|
||||
impl ModulusPoly {
|
||||
|
||||
fn new( field: &ModulusGF, coefficients: &Vec<i32>) -> ModulusPoly {
|
||||
if coefficients.len() == 0 {
|
||||
throw IllegalArgumentException::new();
|
||||
}
|
||||
let .field = field;
|
||||
let coefficients_length: i32 = coefficients.len();
|
||||
if coefficients_length > 1 && coefficients[0] == 0 {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
let first_non_zero: i32 = 1;
|
||||
while first_non_zero < coefficients_length && coefficients[first_non_zero] == 0 {
|
||||
first_non_zero += 1;
|
||||
}
|
||||
if first_non_zero == coefficients_length {
|
||||
let .coefficients = : vec![i32; 1] = vec![0, ]
|
||||
;
|
||||
} else {
|
||||
let .coefficients = : [i32; coefficients_length - first_non_zero] = [0; coefficients_length - first_non_zero];
|
||||
System::arraycopy(&coefficients, first_non_zero, let .coefficients, 0, let .coefficients.len());
|
||||
}
|
||||
} else {
|
||||
let .coefficients = coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_coefficients(&self) -> Vec<i32> {
|
||||
return self.coefficients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
fn get_degree(&self) -> i32 {
|
||||
return self.coefficients.len() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
fn is_zero(&self) -> bool {
|
||||
return self.coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
fn get_coefficient(&self, degree: i32) -> i32 {
|
||||
return self.coefficients[self.coefficients.len() - 1 - degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
fn evaluate_at(&self, a: i32) -> i32 {
|
||||
if a == 0 {
|
||||
// Just return the x^0 coefficient
|
||||
return self.get_coefficient(0);
|
||||
}
|
||||
if a == 1 {
|
||||
// Just the sum of the coefficients
|
||||
let mut result: i32 = 0;
|
||||
for let coefficient: i32 in self.coefficients {
|
||||
result = self.field.add(result, coefficient);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let mut result: i32 = self.coefficients[0];
|
||||
let size: i32 = self.coefficients.len();
|
||||
{
|
||||
let mut i: i32 = 1;
|
||||
while i < size {
|
||||
{
|
||||
result = self.field.add(&self.field.multiply(a, result), self.coefficients[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn add(&self, other: &ModulusPoly) -> ModulusPoly {
|
||||
if !self.field.equals(other.field) {
|
||||
throw IllegalArgumentException::new("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if self.is_zero() {
|
||||
return other;
|
||||
}
|
||||
if other.is_zero() {
|
||||
return self;
|
||||
}
|
||||
let smaller_coefficients: Vec<i32> = self.coefficients;
|
||||
let larger_coefficients: Vec<i32> = other.coefficients;
|
||||
if smaller_coefficients.len() > larger_coefficients.len() {
|
||||
let temp: Vec<i32> = smaller_coefficients;
|
||||
smaller_coefficients = larger_coefficients;
|
||||
larger_coefficients = temp;
|
||||
}
|
||||
let sum_diff: [i32; larger_coefficients.len()] = [0; larger_coefficients.len()];
|
||||
let length_diff: i32 = larger_coefficients.len() - smaller_coefficients.len();
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
System::arraycopy(&larger_coefficients, 0, &sum_diff, 0, length_diff);
|
||||
{
|
||||
let mut i: i32 = length_diff;
|
||||
while i < larger_coefficients.len() {
|
||||
{
|
||||
sum_diff[i] = self.field.add(smaller_coefficients[i - length_diff], larger_coefficients[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ModulusPoly::new(self.field, &sum_diff);
|
||||
}
|
||||
|
||||
fn subtract(&self, other: &ModulusPoly) -> ModulusPoly {
|
||||
if !self.field.equals(other.field) {
|
||||
throw IllegalArgumentException::new("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if other.is_zero() {
|
||||
return self;
|
||||
}
|
||||
return self.add(&other.negative());
|
||||
}
|
||||
|
||||
fn multiply(&self, other: &ModulusPoly) -> ModulusPoly {
|
||||
if !self.field.equals(other.field) {
|
||||
throw IllegalArgumentException::new("ModulusPolys do not have same ModulusGF field");
|
||||
}
|
||||
if self.is_zero() || other.is_zero() {
|
||||
return self.field.get_zero();
|
||||
}
|
||||
let a_coefficients: Vec<i32> = self.coefficients;
|
||||
let a_length: i32 = a_coefficients.len();
|
||||
let b_coefficients: Vec<i32> = other.coefficients;
|
||||
let b_length: i32 = b_coefficients.len();
|
||||
let mut product: [i32; a_length + b_length - 1] = [0; a_length + b_length - 1];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < a_length {
|
||||
{
|
||||
let a_coeff: i32 = a_coefficients[i];
|
||||
{
|
||||
let mut j: i32 = 0;
|
||||
while j < b_length {
|
||||
{
|
||||
product[i + j] = self.field.add(product[i + j], &self.field.multiply(a_coeff, b_coefficients[j]));
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ModulusPoly::new(self.field, &product);
|
||||
}
|
||||
|
||||
fn negative(&self) -> ModulusPoly {
|
||||
let size: i32 = self.coefficients.len();
|
||||
let negative_coefficients: [i32; size] = [0; size];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
negative_coefficients[i] = self.field.subtract(0, self.coefficients[i]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ModulusPoly::new(self.field, &negative_coefficients);
|
||||
}
|
||||
|
||||
fn multiply(&self, scalar: i32) -> ModulusPoly {
|
||||
if scalar == 0 {
|
||||
return self.field.get_zero();
|
||||
}
|
||||
if scalar == 1 {
|
||||
return self;
|
||||
}
|
||||
let size: i32 = self.coefficients.len();
|
||||
let mut product: [i32; size] = [0; size];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
product[i] = self.field.multiply(self.coefficients[i], scalar);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ModulusPoly::new(self.field, &product);
|
||||
}
|
||||
|
||||
fn multiply_by_monomial(&self, degree: i32, coefficient: i32) -> ModulusPoly {
|
||||
if degree < 0 {
|
||||
throw IllegalArgumentException::new();
|
||||
}
|
||||
if coefficient == 0 {
|
||||
return self.field.get_zero();
|
||||
}
|
||||
let size: i32 = self.coefficients.len();
|
||||
let mut product: [i32; size + degree] = [0; size + degree];
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < size {
|
||||
{
|
||||
product[i] = self.field.multiply(self.coefficients[i], coefficient);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ModulusPoly::new(self.field, &product);
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
let result: StringBuilder = StringBuilder::new(8 * self.get_degree());
|
||||
{
|
||||
let mut degree: i32 = self.get_degree();
|
||||
while degree >= 0 {
|
||||
{
|
||||
let mut coefficient: i32 = self.get_coefficient(degree);
|
||||
if coefficient != 0 {
|
||||
if coefficient < 0 {
|
||||
result.append(" - ");
|
||||
coefficient = -coefficient;
|
||||
} else {
|
||||
if result.length() > 0 {
|
||||
result.append(" + ");
|
||||
}
|
||||
}
|
||||
if degree == 0 || coefficient != 1 {
|
||||
result.append(coefficient);
|
||||
}
|
||||
if degree != 0 {
|
||||
if degree == 1 {
|
||||
result.append('x');
|
||||
} else {
|
||||
result.append("x^");
|
||||
result.append(degree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
degree -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
use crate::{BinaryBitmap,NotFoundException,DecodeHintType,ResultPoint};
|
||||
use crate::common::BitMatrix;
|
||||
|
||||
// NEW FILE: detector.rs
|
||||
/*
|
||||
* 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::pdf417::detector;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect a PDF417 Code in an image, even if the
|
||||
* PDF417 Code is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author SITA Lab (kevin.osullivan@sita.aero)
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Guenther Grau
|
||||
*/
|
||||
|
||||
const INDEXES_START_PATTERN: vec![Vec<i32>; 4] = vec![0, 4, 1, 5, ]
|
||||
;
|
||||
|
||||
const INDEXES_STOP_PATTERN: vec![Vec<i32>; 4] = vec![6, 2, 7, 3, ]
|
||||
;
|
||||
|
||||
const MAX_AVG_VARIANCE: f32 = 0.42f;
|
||||
|
||||
const MAX_INDIVIDUAL_VARIANCE: f32 = 0.8f;
|
||||
|
||||
// B S B S B S B S Bar/Space pattern
|
||||
// 11111111 0 1 0 1 0 1 000
|
||||
const START_PATTERN: vec![Vec<i32>; 8] = vec![8, 1, 1, 1, 1, 1, 1, 3, ]
|
||||
;
|
||||
|
||||
// 1111111 0 1 000 1 0 1 00 1
|
||||
const STOP_PATTERN: vec![Vec<i32>; 9] = vec![7, 1, 1, 3, 1, 1, 1, 2, 1, ]
|
||||
;
|
||||
|
||||
const MAX_PIXEL_DRIFT: i32 = 3;
|
||||
|
||||
const MAX_PATTERN_DRIFT: i32 = 5;
|
||||
|
||||
// if we set the value too low, then we don't detect the correct height of the bar if the start patterns are damaged.
|
||||
// if we set the value too high, then we might detect the start pattern from a neighbor barcode.
|
||||
const SKIPPED_ROW_COUNT_MAX: i32 = 25;
|
||||
|
||||
// A PDF471 barcode should have at least 3 rows, with each row being >= 3 times the module width.
|
||||
// Therefore it should be at least 9 pixels tall. To be conservative, we use about half the size to
|
||||
// ensure we don't miss it.
|
||||
const ROW_STEP: i32 = 5;
|
||||
|
||||
const BARCODE_MIN_HEIGHT: i32 = 10;
|
||||
|
||||
const ROTATIONS: vec![Vec<i32>; 4] = vec![0, 180, 270, 90, ]
|
||||
;
|
||||
pub struct Detector {
|
||||
}
|
||||
|
||||
impl Detector {
|
||||
|
||||
fn new() -> Detector {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a PDF417 Code in an image. Checks 0, 90, 180, and 270 degree rotations.</p>
|
||||
*
|
||||
* @param image barcode image to decode
|
||||
* @param hints optional hints to detector
|
||||
* @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will
|
||||
* be found and returned
|
||||
* @return {@link PDF417DetectorResult} encapsulating results of detecting a PDF417 code
|
||||
* @throws NotFoundException if no PDF417 Code can be found
|
||||
*/
|
||||
pub fn detect( image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>, multiple: bool) -> /* throws NotFoundException */Result<PDF417DetectorResult, Rc<Exception>> {
|
||||
// TODO detection improvement, tryHarder could try several different luminance thresholds/blackpoints or even
|
||||
// different binarizers
|
||||
//boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||
let original_matrix: BitMatrix = image.get_black_matrix();
|
||||
for let rotation: i32 in ROTATIONS {
|
||||
let bit_matrix: BitMatrix = ::apply_rotation(original_matrix, rotation);
|
||||
let barcode_coordinates: List<Vec<ResultPoint>> = ::detect(multiple, bit_matrix);
|
||||
if !barcode_coordinates.is_empty() {
|
||||
return Ok(PDF417DetectorResult::new(bit_matrix, &barcode_coordinates, rotation));
|
||||
}
|
||||
}
|
||||
return Ok(PDF417DetectorResult::new(original_matrix, ArrayList<>::new(), 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a rotation to the supplied BitMatrix.
|
||||
* @param matrix bit matrix to apply rotation to
|
||||
* @param rotation the degrees of rotation to apply
|
||||
* @return BitMatrix with applied rotation
|
||||
*/
|
||||
fn apply_rotation( matrix: &BitMatrix, rotation: i32) -> BitMatrix {
|
||||
if rotation % 360 == 0 {
|
||||
return matrix;
|
||||
}
|
||||
let new_matrix: BitMatrix = matrix.clone();
|
||||
new_matrix.rotate(rotation);
|
||||
return new_matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects PDF417 codes in an image. Only checks 0 degree rotation
|
||||
* @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will
|
||||
* be found and returned
|
||||
* @param bitMatrix bit matrix to detect barcodes in
|
||||
* @return List of ResultPoint arrays containing the coordinates of found barcodes
|
||||
*/
|
||||
fn detect( multiple: bool, bit_matrix: &BitMatrix) -> List<Vec<ResultPoint>> {
|
||||
let barcode_coordinates: List<Vec<ResultPoint>> = ArrayList<>::new();
|
||||
let mut row: i32 = 0;
|
||||
let mut column: i32 = 0;
|
||||
let found_barcode_in_row: bool = false;
|
||||
while row < bit_matrix.get_height() {
|
||||
let vertices: Vec<ResultPoint> = ::find_vertices(bit_matrix, row, column);
|
||||
if vertices[0] == null && vertices[3] == null {
|
||||
if !found_barcode_in_row {
|
||||
// we didn't find any barcode so that's the end of searching
|
||||
break;
|
||||
}
|
||||
// we didn't find a barcode starting at the given column and row. Try again from the first column and slightly
|
||||
// below the lowest barcode we found so far.
|
||||
found_barcode_in_row = false;
|
||||
column = 0;
|
||||
for let barcode_coordinate: Vec<ResultPoint> in barcode_coordinates {
|
||||
if barcode_coordinate[1] != null {
|
||||
row = Math::max(row, &barcode_coordinate[1].get_y()) as i32;
|
||||
}
|
||||
if barcode_coordinate[3] != null {
|
||||
row = Math::max(row, barcode_coordinate[3].get_y() as i32);
|
||||
}
|
||||
}
|
||||
row += ROW_STEP;
|
||||
continue;
|
||||
}
|
||||
found_barcode_in_row = true;
|
||||
barcode_coordinates.add(vertices);
|
||||
if !multiple {
|
||||
break;
|
||||
}
|
||||
// start pattern of the barcode just found.
|
||||
if vertices[2] != null {
|
||||
column = vertices[2].get_x() as i32;
|
||||
row = vertices[2].get_y() as i32;
|
||||
} else {
|
||||
column = vertices[4].get_x() as i32;
|
||||
row = vertices[4].get_y() as i32;
|
||||
}
|
||||
}
|
||||
return Ok(barcode_coordinates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the vertices and the codewords area of a black blob using the Start
|
||||
* and Stop patterns as locators.
|
||||
*
|
||||
* @param matrix the scanned barcode image.
|
||||
* @return an array containing the vertices:
|
||||
* vertices[0] x, y top left barcode
|
||||
* vertices[1] x, y bottom left barcode
|
||||
* vertices[2] x, y top right barcode
|
||||
* vertices[3] x, y bottom right barcode
|
||||
* vertices[4] x, y top left codeword area
|
||||
* vertices[5] x, y bottom left codeword area
|
||||
* vertices[6] x, y top right codeword area
|
||||
* vertices[7] x, y bottom right codeword area
|
||||
*/
|
||||
fn find_vertices( matrix: &BitMatrix, start_row: i32, start_column: i32) -> Vec<ResultPoint> {
|
||||
let height: i32 = matrix.get_height();
|
||||
let width: i32 = matrix.get_width();
|
||||
let result: [Option<ResultPoint>; 8] = [None; 8];
|
||||
::copy_to_result(result, &::find_rows_with_pattern(matrix, height, width, start_row, start_column, &START_PATTERN), &INDEXES_START_PATTERN);
|
||||
if result[4] != null {
|
||||
start_column = result[4].get_x() as i32;
|
||||
start_row = result[4].get_y() as i32;
|
||||
}
|
||||
::copy_to_result(result, &::find_rows_with_pattern(matrix, height, width, start_row, start_column, &STOP_PATTERN), &INDEXES_STOP_PATTERN);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn copy_to_result( result: &Vec<ResultPoint>, tmp_result: &Vec<ResultPoint>, destination_indexes: &Vec<i32>) {
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < destination_indexes.len() {
|
||||
{
|
||||
result[destination_indexes[i]] = tmp_result[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn find_rows_with_pattern( matrix: &BitMatrix, height: i32, width: i32, start_row: i32, start_column: i32, pattern: &Vec<i32>) -> Vec<ResultPoint> {
|
||||
let mut result: [Option<ResultPoint>; 4] = [None; 4];
|
||||
let mut found: bool = false;
|
||||
let counters: [i32; pattern.len()] = [0; pattern.len()];
|
||||
while start_row < height {
|
||||
{
|
||||
let mut loc: Vec<i32> = ::find_guard_pattern(matrix, start_column, start_row, width, &pattern, &counters);
|
||||
if loc != null {
|
||||
while start_row > 0 {
|
||||
let previous_row_loc: Vec<i32> = ::find_guard_pattern(matrix, start_column, start_row -= 1, width, &pattern, &counters);
|
||||
if previous_row_loc != null {
|
||||
loc = previous_row_loc;
|
||||
} else {
|
||||
start_row += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result[0] = ResultPoint::new(loc[0], start_row);
|
||||
result[1] = ResultPoint::new(loc[1], start_row);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
start_row += ROW_STEP;
|
||||
}
|
||||
|
||||
let stop_row: i32 = start_row + 1;
|
||||
// Last row of the current symbol that contains pattern
|
||||
if found {
|
||||
let skipped_row_count: i32 = 0;
|
||||
let previous_row_loc: vec![Vec<i32>; 2] = vec![result[0].get_x() as i32, result[1].get_x() as i32, ]
|
||||
;
|
||||
while stop_row < height {
|
||||
{
|
||||
let loc: Vec<i32> = ::find_guard_pattern(matrix, previous_row_loc[0], stop_row, width, &pattern, &counters);
|
||||
// larger drift and don't check for skipped rows.
|
||||
if loc != null && Math::abs(previous_row_loc[0] - loc[0]) < MAX_PATTERN_DRIFT && Math::abs(previous_row_loc[1] - loc[1]) < MAX_PATTERN_DRIFT {
|
||||
previous_row_loc = loc;
|
||||
skipped_row_count = 0;
|
||||
} else {
|
||||
if skipped_row_count > SKIPPED_ROW_COUNT_MAX {
|
||||
break;
|
||||
} else {
|
||||
skipped_row_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop_row += 1;
|
||||
}
|
||||
|
||||
stop_row -= skipped_row_count + 1;
|
||||
result[2] = ResultPoint::new(previous_row_loc[0], stop_row);
|
||||
result[3] = ResultPoint::new(previous_row_loc[1], stop_row);
|
||||
}
|
||||
if stop_row - start_row < BARCODE_MIN_HEIGHT {
|
||||
Arrays::fill(result, null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param matrix row of black/white values to search
|
||||
* @param column x position to start search
|
||||
* @param row y position to start search
|
||||
* @param width the number of pixels to search on this row
|
||||
* @param pattern pattern of counts of number of black and white pixels that are
|
||||
* being searched for as a pattern
|
||||
* @param counters array of counters, as long as pattern, to re-use
|
||||
* @return start/end horizontal offset of guard pattern, as an array of two ints.
|
||||
*/
|
||||
fn find_guard_pattern( matrix: &BitMatrix, column: i32, row: i32, width: i32, pattern: &Vec<i32>, counters: &Vec<i32>) -> Vec<i32> {
|
||||
Arrays::fill(&counters, 0, counters.len(), 0);
|
||||
let pattern_start: i32 = column;
|
||||
let pixel_drift: i32 = 0;
|
||||
// if there are black pixels left of the current pixel shift to the left, but only for MAX_PIXEL_DRIFT pixels
|
||||
while matrix.get(pattern_start, row) && pattern_start > 0 && pixel_drift += 1 !!!check!!! post increment < MAX_PIXEL_DRIFT {
|
||||
pattern_start -= 1;
|
||||
}
|
||||
let mut x: i32 = pattern_start;
|
||||
let counter_position: i32 = 0;
|
||||
let pattern_length: i32 = pattern.len();
|
||||
{
|
||||
let is_white: bool = false;
|
||||
while x < width {
|
||||
{
|
||||
let pixel: bool = matrix.get(x, row);
|
||||
if pixel != is_white {
|
||||
counters[counter_position] += 1;
|
||||
} else {
|
||||
if counter_position == pattern_length - 1 {
|
||||
if ::pattern_match_variance(&counters, &pattern) < MAX_AVG_VARIANCE {
|
||||
return : vec![i32; 2] = vec![pattern_start, x, ]
|
||||
;
|
||||
}
|
||||
pattern_start += counters[0] + counters[1];
|
||||
System::arraycopy(&counters, 2, &counters, 0, counter_position - 1);
|
||||
counters[counter_position - 1] = 0;
|
||||
counters[counter_position] = 0;
|
||||
counter_position -= 1;
|
||||
} else {
|
||||
counter_position += 1;
|
||||
}
|
||||
counters[counter_position] = 1;
|
||||
is_white = !is_white;
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if counter_position == pattern_length - 1 && ::pattern_match_variance(&counters, &pattern) < MAX_AVG_VARIANCE {
|
||||
return : vec![i32; 2] = vec![pattern_start, x - 1, ]
|
||||
;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how closely a set of observed counts of runs of black/white
|
||||
* values matches a given target pattern. This is reported as the ratio of
|
||||
* the total variance from the expected pattern proportions across all
|
||||
* pattern elements, to the length of the pattern.
|
||||
*
|
||||
* @param counters observed counters
|
||||
* @param pattern expected pattern
|
||||
* @return ratio of total variance between counters and pattern compared to total pattern size
|
||||
*/
|
||||
fn pattern_match_variance( counters: &Vec<i32>, pattern: &Vec<i32>) -> f32 {
|
||||
let num_counters: i32 = counters.len();
|
||||
let mut total: i32 = 0;
|
||||
let pattern_length: i32 = 0;
|
||||
{
|
||||
let mut i: i32 = 0;
|
||||
while i < num_counters {
|
||||
{
|
||||
total += counters[i];
|
||||
pattern_length += pattern[i];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if total < pattern_length {
|
||||
// is too small to reliably match, so fail:
|
||||
return Float::POSITIVE_INFINITY;
|
||||
}
|
||||
// We're going to fake floating-point math in integers. We just need to use more bits.
|
||||
// Scale up patternLength so that intermediate values below like scaledCounter will have
|
||||
// more "significant digits".
|
||||
let unit_bar_width: f32 = total as f32 / pattern_length;
|
||||
let max_individual_variance: f32 = MAX_INDIVIDUAL_VARIANCE * unit_bar_width;
|
||||
let total_variance: f32 = 0.0f;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < num_counters {
|
||||
{
|
||||
let counter: i32 = counters[x];
|
||||
let scaled_pattern: f32 = pattern[x] * unit_bar_width;
|
||||
let variance: f32 = if counter > scaled_pattern { counter - scaled_pattern } else { scaled_pattern - counter };
|
||||
if variance > max_individual_variance {
|
||||
return Float::POSITIVE_INFINITY;
|
||||
}
|
||||
total_variance += variance;
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return total_variance / total;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: p_d_f417_detector_result.rs
|
||||
/*
|
||||
* 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::pdf417::detector;
|
||||
|
||||
/**
|
||||
* @author Guenther Grau
|
||||
*/
|
||||
pub struct PDF417DetectorResult {
|
||||
|
||||
let bits: BitMatrix;
|
||||
|
||||
let points: List<Vec<ResultPoint>>;
|
||||
|
||||
let rotation: i32;
|
||||
}
|
||||
|
||||
impl PDF417DetectorResult {
|
||||
|
||||
pub fn new( bits: &BitMatrix, points: &List<Vec<ResultPoint>>, rotation: i32) -> PDF417DetectorResult {
|
||||
let .bits = bits;
|
||||
let .points = points;
|
||||
let .rotation = rotation;
|
||||
}
|
||||
|
||||
pub fn new( bits: &BitMatrix, points: &List<Vec<ResultPoint>>) -> PDF417DetectorResult {
|
||||
this(bits, &points, 0);
|
||||
}
|
||||
|
||||
pub fn get_bits(&self) -> BitMatrix {
|
||||
return self.bits;
|
||||
}
|
||||
|
||||
pub fn get_points(&self) -> List<Vec<ResultPoint>> {
|
||||
return self.points;
|
||||
}
|
||||
|
||||
pub fn get_rotation(&self) -> i32 {
|
||||
return self.rotation;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
316
src/qrcode.rs
316
src/qrcode.rs
@@ -1,316 +0,0 @@
|
||||
use crate::{BarcodeFormat,BinaryBitmap,ChecksumException,DecodeHintType,FormatException,NotFoundException,Reader,XRingResult,ResultMetadataType,ResultPoint,EncodeHintType,Writer,WriterException,};
|
||||
use crate::common::{BitMatrix,DecoderResult,DetectorResult,};
|
||||
use crate::qrcode::decoder::{Decoder,QRCodeDecoderMetaData};
|
||||
use crate::qrcode::detector::{Detector};
|
||||
use crate::qrcode::encoder::{ByteMatrix,ErrorCorrectionLevel,Encoder,QRCode};
|
||||
|
||||
// NEW FILE: q_r_code_reader.rs
|
||||
/*
|
||||
* 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::qrcode;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode QR Codes in an image.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
const NO_POINTS: [Option<ResultPoint>; 0] = [None; 0];
|
||||
#[derive(Reader)]
|
||||
pub struct QRCodeReader {
|
||||
|
||||
let decoder: Decoder = Decoder::new();
|
||||
}
|
||||
|
||||
impl QRCodeReader {
|
||||
|
||||
pub fn get_decoder(&self) -> Decoder {
|
||||
return self.decoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and decodes a QR code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the QR code
|
||||
* @throws NotFoundException if a QR code cannot be found
|
||||
* @throws FormatException if a QR code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
pub fn decode(&self, image: &BinaryBitmap) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||
return Ok(self.decode(image, null));
|
||||
}
|
||||
|
||||
pub fn decode(&self, image: &BinaryBitmap, hints: &Map<DecodeHintType, ?>) -> /* throws NotFoundException, ChecksumException, FormatException */Result<Result, Rc<Exception>> {
|
||||
let decoder_result: DecoderResult;
|
||||
let mut points: Vec<ResultPoint>;
|
||||
if hints != null && hints.contains_key(DecodeHintType::PURE_BARCODE) {
|
||||
let bits: BitMatrix = ::extract_pure_bits(&image.get_black_matrix());
|
||||
decoder_result = self.decoder.decode(bits, &hints);
|
||||
points = NO_POINTS;
|
||||
} else {
|
||||
let detector_result: DetectorResult = Detector::new(&image.get_black_matrix()).detect(&hints);
|
||||
decoder_result = self.decoder.decode(&detector_result.get_bits(), &hints);
|
||||
points = detector_result.get_points();
|
||||
}
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if decoder_result.get_other() instanceof QRCodeDecoderMetaData {
|
||||
(decoder_result.get_other() as QRCodeDecoderMetaData).apply_mirrored_correction(points);
|
||||
}
|
||||
let result: Result = Result::new(&decoder_result.get_text(), &decoder_result.get_raw_bytes(), points, BarcodeFormat::QR_CODE);
|
||||
let byte_segments: List<Vec<i8>> = decoder_result.get_byte_segments();
|
||||
if byte_segments != null {
|
||||
result.put_metadata(ResultMetadataType::BYTE_SEGMENTS, &byte_segments);
|
||||
}
|
||||
let ec_level: String = decoder_result.get_e_c_level();
|
||||
if ec_level != null {
|
||||
result.put_metadata(ResultMetadataType::ERROR_CORRECTION_LEVEL, &ec_level);
|
||||
}
|
||||
if decoder_result.has_structured_append() {
|
||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_SEQUENCE, &decoder_result.get_structured_append_sequence_number());
|
||||
result.put_metadata(ResultMetadataType::STRUCTURED_APPEND_PARITY, &decoder_result.get_structured_append_parity());
|
||||
}
|
||||
result.put_metadata(ResultMetadataType::SYMBOLOGY_IDENTIFIER, format!("]Q{}", decoder_result.get_symbology_modifier()));
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
fn extract_pure_bits( image: &BitMatrix) -> /* throws NotFoundException */Result<BitMatrix, Rc<Exception>> {
|
||||
let left_top_black: Vec<i32> = image.get_top_left_on_bit();
|
||||
let right_bottom_black: Vec<i32> = image.get_bottom_right_on_bit();
|
||||
if left_top_black == null || right_bottom_black == null {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
let module_size: f32 = self.module_size(&left_top_black, image);
|
||||
let mut top: i32 = left_top_black[1];
|
||||
let bottom: i32 = right_bottom_black[1];
|
||||
let mut left: i32 = left_top_black[0];
|
||||
let mut right: i32 = right_bottom_black[0];
|
||||
// Sanity check!
|
||||
if left >= right || top >= bottom {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
if bottom - top != right - left {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
right = left + (bottom - top);
|
||||
if right >= image.get_width() {
|
||||
// Abort if that would not make sense -- off image
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
}
|
||||
let matrix_width: i32 = Math::round((right - left + 1.0) / module_size);
|
||||
let matrix_height: i32 = Math::round((bottom - top + 1.0) / module_size);
|
||||
if matrix_width <= 0 || matrix_height <= 0 {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
if matrix_height != matrix_width {
|
||||
// Only possibly decode square regions
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
let nudge: i32 = (module_size / 2.0f) as i32;
|
||||
top += nudge;
|
||||
left += nudge;
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
let nudged_too_far_right: i32 = left + ((matrix_width - 1.0) * module_size) as i32 - right;
|
||||
if nudged_too_far_right > 0 {
|
||||
if nudged_too_far_right > nudge {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
left -= nudged_too_far_right;
|
||||
}
|
||||
// See logic above
|
||||
let nudged_too_far_down: i32 = top + ((matrix_height - 1.0) * module_size) as i32 - bottom;
|
||||
if nudged_too_far_down > 0 {
|
||||
if nudged_too_far_down > nudge {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
top -= nudged_too_far_down;
|
||||
}
|
||||
// Now just read off the bits
|
||||
let bits: BitMatrix = BitMatrix::new(matrix_width, matrix_height);
|
||||
{
|
||||
let mut y: i32 = 0;
|
||||
while y < matrix_height {
|
||||
{
|
||||
let i_offset: i32 = top + (y * module_size) as i32;
|
||||
{
|
||||
let mut x: i32 = 0;
|
||||
while x < matrix_width {
|
||||
{
|
||||
if image.get(left + (x * module_size) as i32, i_offset) {
|
||||
bits.set(x, y);
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(bits);
|
||||
}
|
||||
|
||||
fn module_size( left_top_black: &Vec<i32>, image: &BitMatrix) -> /* throws NotFoundException */Result<f32, Rc<Exception>> {
|
||||
let height: i32 = image.get_height();
|
||||
let width: i32 = image.get_width();
|
||||
let mut x: i32 = left_top_black[0];
|
||||
let mut y: i32 = left_top_black[1];
|
||||
let in_black: bool = true;
|
||||
let mut transitions: i32 = 0;
|
||||
while x < width && y < height {
|
||||
if in_black != image.get(x, y) {
|
||||
if transitions += 1 == 5 {
|
||||
break;
|
||||
}
|
||||
in_black = !in_black;
|
||||
}
|
||||
x += 1;
|
||||
y += 1;
|
||||
}
|
||||
if x == width || y == height {
|
||||
throw NotFoundException::get_not_found_instance();
|
||||
}
|
||||
return Ok((x - left_top_black[0]) / 7.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// NEW FILE: q_r_code_writer.rs
|
||||
/*
|
||||
* 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::qrcode;
|
||||
|
||||
/**
|
||||
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
|
||||
const QUIET_ZONE_SIZE: i32 = 4;
|
||||
#[derive(Writer)]
|
||||
pub struct QRCodeWriter {
|
||||
}
|
||||
|
||||
impl QRCodeWriter {
|
||||
|
||||
pub fn encode(&self, contents: &String, format: &BarcodeFormat, width: i32, height: i32) -> /* throws WriterException */Result<BitMatrix, Rc<Exception>> {
|
||||
return Ok(self.encode(&contents, format, width, height, null));
|
||||
}
|
||||
|
||||
pub fn encode(&self, contents: &String, format: &BarcodeFormat, width: i32, height: i32, hints: &Map<EncodeHintType, ?>) -> /* throws WriterException */Result<BitMatrix, Rc<Exception>> {
|
||||
if contents.is_empty() {
|
||||
throw IllegalArgumentException::new("Found empty contents");
|
||||
}
|
||||
if format != BarcodeFormat::QR_CODE {
|
||||
throw IllegalArgumentException::new(format!("Can only encode QR_CODE, but got {}", format));
|
||||
}
|
||||
if width < 0 || height < 0 {
|
||||
throw IllegalArgumentException::new(format!("Requested dimensions are too small: {}x{}", width, height));
|
||||
}
|
||||
let error_correction_level: ErrorCorrectionLevel = ErrorCorrectionLevel::L;
|
||||
let quiet_zone: i32 = QUIET_ZONE_SIZE;
|
||||
if hints != null {
|
||||
if hints.contains_key(EncodeHintType::ERROR_CORRECTION) {
|
||||
error_correction_level = ErrorCorrectionLevel::value_of(&hints.get(EncodeHintType::ERROR_CORRECTION).to_string());
|
||||
}
|
||||
if hints.contains_key(EncodeHintType::MARGIN) {
|
||||
quiet_zone = Integer::parse_int(&hints.get(EncodeHintType::MARGIN).to_string());
|
||||
}
|
||||
}
|
||||
let code: QRCode = Encoder::encode(&contents, error_correction_level, &hints);
|
||||
return Ok(::render_result(code, width, height, quiet_zone));
|
||||
}
|
||||
|
||||
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
|
||||
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
|
||||
fn render_result( code: &QRCode, width: i32, height: i32, quiet_zone: i32) -> BitMatrix {
|
||||
let input: ByteMatrix = code.get_matrix();
|
||||
if input == null {
|
||||
throw IllegalStateException::new();
|
||||
}
|
||||
let input_width: i32 = input.get_width();
|
||||
let input_height: i32 = input.get_height();
|
||||
let qr_width: i32 = input_width + (quiet_zone * 2);
|
||||
let qr_height: i32 = input_height + (quiet_zone * 2);
|
||||
let output_width: i32 = Math::max(width, qr_width);
|
||||
let output_height: i32 = Math::max(height, qr_height);
|
||||
let multiple: i32 = Math::min(output_width / qr_width, output_height / qr_height);
|
||||
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
|
||||
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
|
||||
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
|
||||
// handle all the padding from 100x100 (the actual QR) up to 200x160.
|
||||
let left_padding: i32 = (output_width - (input_width * multiple)) / 2;
|
||||
let top_padding: i32 = (output_height - (input_height * multiple)) / 2;
|
||||
let output: BitMatrix = BitMatrix::new(output_width, output_height);
|
||||
{
|
||||
let input_y: i32 = 0, let output_y: i32 = top_padding;
|
||||
while input_y < input_height {
|
||||
{
|
||||
// Write the contents of this row of the barcode
|
||||
{
|
||||
let input_x: i32 = 0, let output_x: i32 = left_padding;
|
||||
while input_x < input_width {
|
||||
{
|
||||
if input.get(input_x, input_y) == 1 {
|
||||
output.set_region(output_x, output_y, multiple, multiple);
|
||||
}
|
||||
}
|
||||
input_x += 1;
|
||||
output_x += multiple;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
input_y += 1;
|
||||
output_y += multiple;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user