diff --git a/src/maxicode/MaxiCodeReader.java b/src/maxicode/MaxiCodeReader.java deleted file mode 100644 index 7c7887f..0000000 --- a/src/maxicode/MaxiCodeReader.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2011 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.maxicode; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.BinaryBitmap; -import com.google.zxing.ChecksumException; -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.RXingResult; -import com.google.zxing.RXingResultMetadataType; -import com.google.zxing.RXingResultPoint; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderRXingResult; -import com.google.zxing.maxicode.decoder.Decoder; - -import java.util.Map; - -/** - * This implementation can detect and decode a MaxiCode in an image. - */ -public final class MaxiCodeReader implements Reader { - - private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0]; - private static final int MATRIX_WIDTH = 30; - private static final int MATRIX_HEIGHT = 33; - - private final Decoder decoder = new Decoder(); - - /** - * 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 - */ - @Override - public RXingResult decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException { - return decode(image, null); - } - - @Override - public RXingResult decode(BinaryBitmap image, Map hints) - throws NotFoundException, ChecksumException, FormatException { - // Note that MaxiCode reader effectively always assumes PURE_BARCODE mode - // and can't detect it in an image - BitMatrix bits = extractPureBits(image.getBlackMatrix()); - DecoderRXingResult decoderRXingResult = decoder.decode(bits, hints); - RXingResult result = new RXingResult(decoderRXingResult.getText(), decoderRXingResult.getRawBytes(), NO_POINTS, BarcodeFormat.MAXICODE); - - String ecLevel = decoderRXingResult.getECLevel(); - if (ecLevel != null) { - result.putMetadata(RXingResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel); - } - return result; - } - - @Override - public void reset() { - // 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. - */ - private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException { - - int[] enclosingRectangle = image.getEnclosingRectangle(); - if (enclosingRectangle == null) { - throw NotFoundException.getNotFoundInstance(); - } - - int left = enclosingRectangle[0]; - int top = enclosingRectangle[1]; - int width = enclosingRectangle[2]; - int height = enclosingRectangle[3]; - - // Now just read off the bits - BitMatrix bits = new BitMatrix(MATRIX_WIDTH, MATRIX_HEIGHT); - for (int y = 0; y < MATRIX_HEIGHT; y++) { - int iy = Math.min(top + (y * height + height / 2) / MATRIX_HEIGHT, height - 1); - for (int x = 0; x < MATRIX_WIDTH; x++) { - // 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. - int ix = left + Math.min( - (x * width + width / 2 + (y & 0x01) * width / 2) / MATRIX_WIDTH, - width - 1); - if (image.get(ix, iy)) { - bits.set(x, y); - } - } - } - return bits; - } - -} diff --git a/src/maxicode/decoder/decoded_bit_stream_parser.rs b/src/maxicode/decoder/decoded_bit_stream_parser.rs index d88e0ab..03c1a5c 100644 --- a/src/maxicode/decoder/decoded_bit_stream_parser.rs +++ b/src/maxicode/decoder/decoded_bit_stream_parser.rs @@ -81,7 +81,7 @@ static ref SETS : [String;5]= [ ]; } -pub fn decode(bytes: &[u8], mode: u32) -> Result { +pub fn decode(bytes: &[u8], mode: u8) -> Result { let mut result = String::with_capacity(144); match mode { 2 | 3 => { @@ -216,15 +216,15 @@ fn getMessage(bytes: &[u8], start: u32, len: u32) -> String { NS => { // let nsval = (bytes[++i] << 24) + (bytes[++i] << 18) + (bytes[++i] << 12) + (bytes[++i] << 6) + bytes[++i]; i += 1; - let mut nsval = bytes[i as usize] << 24; + let mut nsval = (bytes[i as usize] as u32) << 24; i += 1; - nsval += bytes[i as usize] << 18; + nsval += (bytes[i as usize] as u32) << 18; i += 1; - nsval += bytes[i as usize] << 12; + nsval += (bytes[i as usize] as u32) << 12; i += 1; - nsval += bytes[i as usize] << 6; + nsval += (bytes[i as usize] as u32) << 6; i += 1; - nsval += bytes[i as usize]; + nsval += bytes[i as usize] as u32; // sb.append(new DecimalFormat("000000000").format(nsval));}, sb.push_str(&format!("{:0>9}", nsval)); } @@ -254,8 +254,8 @@ fn subtract_two_single_char_strings(str1: &str, str2: &str) -> usize { let str1_bytes = str1.as_bytes(); let str2_bytes = str2.as_bytes(); - let str1_u16 = ((str1_bytes[0] as u16) << 2) + str1_bytes[1] as u16; - let str2_u16 = ((str2_bytes[0] as u16) << 2) + str2_bytes[1] as u16; + let str1_u16 = ((str1_bytes[0] as usize) << 4) + ((str1_bytes[1] as usize) << 2) + str1_bytes[2] as usize; + let str2_u16 = ((str2_bytes[0] as usize) << 4) + ((str2_bytes[1] as usize) << 2) + str2_bytes[2] as usize; (str1_u16 - str2_u16) as usize } diff --git a/src/maxicode/decoder/decoder.rs b/src/maxicode/decoder/decoder.rs index fd214f3..bd9d62d 100644 --- a/src/maxicode/decoder/decoder.rs +++ b/src/maxicode/decoder/decoder.rs @@ -14,18 +14,19 @@ * limitations under the License. */ -package com.google.zxing.maxicode.decoder; +use std::collections::HashMap; -import com.google.zxing.ChecksumException; -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderRXingResult; -import com.google.zxing.common.reedsolomon.GenericGF; -import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; -import com.google.zxing.common.reedsolomon.ReedSolomonException; +use lazy_static::lazy_static; -import java.util.Map; +use crate::{ + common::{ + reedsolomon::{get_predefined_genericgf, PredefinedGenericGF, ReedSolomonDecoder}, + BitMatrix, DecoderRXingResult, + }, + DecodingHintDictionary, Exceptions, +}; + +use super::{decoded_bit_stream_parser, BitMatrixParser}; /** *

The main class which implements MaxiCode decoding -- as opposed to locating and extracting @@ -33,82 +34,86 @@ import java.util.Map; * * @author Manuel Kasten */ -public final class Decoder { - private static final int ALL = 0; - private static final int EVEN = 1; - private static final int ODD = 2; +const ALL: u32 = 0; +const EVEN: u32 = 1; +const ODD: u32 = 2; - private final ReedSolomonDecoder rsDecoder; +lazy_static! { + static ref rsDecoder: ReedSolomonDecoder = ReedSolomonDecoder::new(get_predefined_genericgf( + PredefinedGenericGF::MaxicodeField64 + )); +} - public Decoder() { - rsDecoder = new ReedSolomonDecoder(GenericGF.MAXICODE_FIELD_64); - } +pub fn decode(bits: BitMatrix) -> Result { + decode_with_hints(bits, &HashMap::new()) +} - public DecoderRXingResult decode(BitMatrix bits) throws ChecksumException, FormatException { - return decode(bits, null); - } +pub fn decode_with_hints( + bits: BitMatrix, + _hints: &DecodingHintDictionary, +) -> Result { + let parser = BitMatrixParser::new(bits); + let mut codewords = parser.readCodewords(); - public DecoderRXingResult decode(BitMatrix bits, - Map hints) throws FormatException, ChecksumException { - BitMatrixParser parser = new BitMatrixParser(bits); - byte[] codewords = parser.readCodewords(); - - correctErrors(codewords, 0, 10, 10, ALL); - int mode = codewords[0] & 0x0F; - byte[] datawords; - switch (mode) { - case 2: - case 3: - case 4: - correctErrors(codewords, 20, 84, 40, EVEN); - correctErrors(codewords, 20, 84, 40, ODD); - datawords = new byte[94]; - break; - case 5: - correctErrors(codewords, 20, 68, 56, EVEN); - correctErrors(codewords, 20, 68, 56, ODD); - datawords = new byte[78]; - break; - default: - throw FormatException.getFormatInstance(); + correctErrors(&mut codewords, 0, 10, 10, ALL)?; + let mode = codewords[0] & 0x0F; + let mut datawords; + match mode { + 2 | 3 | 4 => { + correctErrors(&mut codewords, 20, 84, 40, EVEN)?; + correctErrors(&mut codewords, 20, 84, 40, ODD)?; + datawords = vec![0u8; 94]; + } + 5 => { + correctErrors(&mut codewords, 20, 68, 56, EVEN)?; + correctErrors(&mut codewords, 20, 68, 56, ODD)?; + datawords = vec![0u8; 78]; + } + _ => return Err(Exceptions::NotFoundException("".to_owned())), } - System.arraycopy(codewords, 0, datawords, 0, 10); - System.arraycopy(codewords, 20, datawords, 10, datawords.length - 10); + datawords[0..10].clone_from_slice(&codewords[0..10]); + // System.arraycopy(codewords, 0, datawords, 0, 10); + let datawords_len = datawords.len(); + datawords[10..datawords_len].clone_from_slice(&codewords[20..datawords_len + 10]); + // System.arraycopy(codewords, 20, datawords, 10, datawords.length - 10); - return DecodedBitStreamParser.decode(datawords, mode); - } + return decoded_bit_stream_parser::decode(&datawords, mode); +} - private void correctErrors(byte[] codewordBytes, - int start, - int dataCodewords, - int ecCodewords, - int mode) throws ChecksumException { - int codewords = dataCodewords + ecCodewords; +fn correctErrors( + codewordBytes: &mut [u8], + start: u32, + dataCodewords: u32, + ecCodewords: u32, + mode: u32, +) -> Result<(), Exceptions> { + let codewords = dataCodewords + ecCodewords; // in EVEN or ODD mode only half the codewords - int divisor = mode == ALL ? 1 : 2; + let divisor = if mode == ALL { 1 } else { 2 }; // First read into an array of ints - int[] codewordsInts = new int[codewords / divisor]; - for (int i = 0; i < codewords; i++) { - if ((mode == ALL) || (i % 2 == (mode - 1))) { - codewordsInts[i / divisor] = codewordBytes[i + start] & 0xFF; - } - } - try { - rsDecoder.decode(codewordsInts, ecCodewords / divisor); - } catch (ReedSolomonException ignored) { - throw ChecksumException.getChecksumInstance(); + let mut codewordsInts = vec![0i32; (codewords / divisor) as usize]; + for i in 0..codewords { + // for (int i = 0; i < codewords; i++) { + if (mode == ALL) || (i % 2 == (mode - 1)) { + codewordsInts[(i / divisor) as usize] = codewordBytes[(i + start) as usize] as i32; + } } + // try { + rsDecoder.decode(&mut codewordsInts, (ecCodewords / divisor) as i32)?; + // } catch (ReedSolomonException ignored) { + // throw ChecksumException.getChecksumInstance(); + // } // Copy back into array of bytes -- only need to worry about the bytes that were data // We don't care about errors in the error-correction codewords - for (int i = 0; i < dataCodewords; i++) { - if ((mode == ALL) || (i % 2 == (mode - 1))) { - codewordBytes[i + start] = (byte) codewordsInts[i / divisor]; - } + for i in 0..dataCodewords { + // for (int i = 0; i < dataCodewords; i++) { + if (mode == ALL) || (i % 2 == (mode - 1)) { + codewordBytes[(i + start) as usize] = codewordsInts[(i / divisor) as usize] as u8; + } } - } - + Ok(()) } diff --git a/src/maxicode/decoder/mod.rs b/src/maxicode/decoder/mod.rs index d628228..fc1e18c 100644 --- a/src/maxicode/decoder/mod.rs +++ b/src/maxicode/decoder/mod.rs @@ -1,6 +1,6 @@ mod bit_matrix_parser; pub mod decoded_bit_stream_parser; -mod decoder; +pub mod decoder; pub use bit_matrix_parser::*; pub use decoder::*; \ No newline at end of file diff --git a/src/maxicode/maxi_code_reader.rs b/src/maxicode/maxi_code_reader.rs new file mode 100644 index 0000000..ebeb1a8 --- /dev/null +++ b/src/maxicode/maxi_code_reader.rs @@ -0,0 +1,125 @@ +/* + * Copyright 2011 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. + */ + +use std::collections::HashMap; + +use crate::{ + common::BitMatrix, BarcodeFormat, Exceptions, RXingResult, RXingResultMetadataType, Reader, +}; + +use super::decoder::decoder; + +/** + * This implementation can detect and decode a MaxiCode in an image. + */ +pub struct MaxiCodeReader { + // private final Decoder decoder = new 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: &crate::BinaryBitmap) -> Result { + self.decode_with_hints(image, &HashMap::new()) + } + + /** + * 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_with_hints( + &self, + image: &crate::BinaryBitmap, + hints: &crate::DecodingHintDictionary, + ) -> Result { + // Note that MaxiCode reader effectively always assumes PURE_BARCODE mode + // and can't detect it in an image + let bits = Self::extractPureBits(image.getBlackMatrix()?)?; + let decoderRXingResult = decoder::decode_with_hints(bits, &hints)?; + let mut result = RXingResult::new( + decoderRXingResult.getText(), + decoderRXingResult.getRawBytes().clone(), + Vec::new(), + BarcodeFormat::MAXICODE, + ); + + let ecLevel = decoderRXingResult.getECLevel(); + if !ecLevel.is_empty() { + result.putMetadata( + RXingResultMetadataType::ERROR_CORRECTION_LEVEL, + crate::RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned()), + ); + } + Ok(result) + } + + fn reset(&self) { + // do nothing + } +} +impl MaxiCodeReader { + const MATRIX_WIDTH: u32 = 30; + const MATRIX_HEIGHT: u32 = 33; + + /** + * 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 extractPureBits(image: &BitMatrix) -> Result { + let enclosingRectangleOption = image.getEnclosingRectangle(); + if enclosingRectangleOption.is_none() { + return Err(Exceptions::NotFoundException("".to_owned())); + } + + let enclosingRectangle = enclosingRectangleOption.unwrap(); + + let left = enclosingRectangle[0]; + let top = enclosingRectangle[1]; + let width = enclosingRectangle[2]; + let height = enclosingRectangle[3]; + + // Now just read off the bits + let mut bits = BitMatrix::new(Self::MATRIX_WIDTH, Self::MATRIX_HEIGHT)?; + for y in 0..Self::MATRIX_HEIGHT { + // for (int y = 0; y < MATRIX_HEIGHT; y++) { + let iy = (top + (y * height + height / 2) / Self::MATRIX_HEIGHT).min(height - 1); + for x in 0..Self::MATRIX_WIDTH { + // for (int x = 0; x < MATRIX_WIDTH; x++) { + // 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 = left + + ((x * width + width / 2 + (y & 0x01) * width / 2) / Self::MATRIX_WIDTH) + .min(width - 1); + if image.get(ix, iy) { + bits.set(x, y); + } + } + } + Ok(bits) + } +} diff --git a/src/maxicode/mod.rs b/src/maxicode/mod.rs index 2d0278d..d1ac838 100644 --- a/src/maxicode/mod.rs +++ b/src/maxicode/mod.rs @@ -1 +1,4 @@ -mod decoder; \ No newline at end of file +pub mod decoder; +mod maxi_code_reader; + +pub use maxi_code_reader::*; \ No newline at end of file diff --git a/tests/maxicode_blackbox_tests.rs b/tests/maxicode_blackbox_tests.rs new file mode 100644 index 0000000..ee76e09 --- /dev/null +++ b/tests/maxicode_blackbox_tests.rs @@ -0,0 +1,59 @@ +/* + * Copyright 2016 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. + */ + +use rxing::{maxicode::MaxiCodeReader, BarcodeFormat, DecodeHintType}; + +mod common; + +/** + * Tests {@link MaxiCodeReader} against a fixed set of test images. + */ +#[test] +fn maxicode1_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/maxicode-1", + Box::new(MaxiCodeReader {}), + BarcodeFormat::MAXICODE, + ); + // super("src/test/resources/blackbox/maxicode-1", new MultiFormatReader(), BarcodeFormat.MAXICODE); + tester.addTest(6, 6, 0.0); + + tester.testBlackBox(); +} + +/** + * Tests all characters in Set A. + * + * @author Daniel Gredler + * @see Defect 1543 + */ +#[test] +fn maxi_code_black_box1_test_case() { + let mut tester = common::AbstractBlackBoxTestCase::new( + "test_resources/blackbox/maxicode-1", + Box::new(MaxiCodeReader {}), + BarcodeFormat::MAXICODE, + ); + // super("src/test/resources/blackbox/maxicode-1", new MultiFormatReader(), BarcodeFormat.MAXICODE); + tester.addHint( + DecodeHintType::PURE_BARCODE, + rxing::DecodeHintValue::PureBarcode(true), + ); + + tester.addTest(1, 1, 0.0); + + tester.testBlackBox(); +}