From 1c286fc6aebaed977150ba7ebc3d1342188d37fb Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Sat, 4 Mar 2023 14:34:09 -0600 Subject: [PATCH] add `MultiUseMuliFormatReader` This should avoid the issues with frequent reuse of the generic version of the library. It has a much larger upfront cost, so it should only be used if the reader will be used multiple times. The helper functions do not use this reader. Fixes: #23 --- src/lib.rs | 2 + src/multi_use_multi_format_reader.rs | 236 +++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 src/multi_use_multi_format_reader.rs diff --git a/src/lib.rs b/src/lib.rs index dd88971..ffecd3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,8 @@ pub mod pdf417; mod multi_format_writer; pub use multi_format_writer::*; +mod multi_use_multi_format_reader; +pub use multi_use_multi_format_reader::*; mod multi_format_reader; pub use multi_format_reader::*; diff --git a/src/multi_use_multi_format_reader.rs b/src/multi_use_multi_format_reader.rs new file mode 100644 index 0000000..efeec2a --- /dev/null +++ b/src/multi_use_multi_format_reader.rs @@ -0,0 +1,236 @@ +/* + * 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. + */ + + use std::collections::{HashMap, HashSet}; + + use crate::common::Result; + use crate::{ + aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader, + oned::MultiFormatOneDReader, pdf417::PDF417Reader, qrcode::QRCodeReader, BarcodeFormat, + Binarizer, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary, Exceptions, + RXingResult, Reader, + }; + + /** + * MultiFormatReader is a convenience class and the main entry point into the library for most uses. + * By default it attempts to decode all barcode formats that the library supports. Optionally, you + * can provide a hints object to request different behavior, for example only decoding QR codes. + * + * @author Sean Owen + * @author dswitkin@google.com (Daniel Switkin) + */ + #[derive(Default)] + pub struct MultiUseMultiFormatReader { + hints: DecodingHintDictionary, + possible_formats: HashSet, + try_harder: bool, + one_d_reader: MultiFormatOneDReader, + qr_code_reader: QRCodeReader, + data_matrix_reader: DataMatrixReader, + aztec_reader: AztecReader, + pdf417_reader: PDF417Reader, + maxicode_reader: MaxiCodeReader, + + } + + impl Reader for MultiUseMultiFormatReader { + /** + * This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it + * passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly. + * Use setHints() followed by decodeWithState() for continuous scan applications. + * + * @param image The pixel data to decode + * @return The contents of the image + * @throws NotFoundException Any errors which occurred + */ + fn decode(&mut self, image: &mut BinaryBitmap) -> Result { + self.set_hints(&HashMap::new()); + self.decode_internal(image) + } + + /** + * Decode an image using the hints provided. Does not honor existing state. + * + * @param image The pixel data to decode + * @param hints The hints to use, clearing the previous state. + * @return The contents of the image + * @throws NotFoundException Any errors which occurred + */ + fn decode_with_hints( + &mut self, + image: &mut BinaryBitmap, + hints: &DecodingHintDictionary, + ) -> Result { + self.set_hints(hints); + self.decode_internal(image) + } + + fn reset(&mut self) { + self.one_d_reader.reset(); + self.qr_code_reader.reset(); + self.data_matrix_reader.reset(); + self.aztec_reader.reset(); + self.pdf417_reader.reset(); + self.maxicode_reader.reset(); + } + } + + impl MultiUseMultiFormatReader { + /** + * Decode an image using the state set up by calling setHints() previously. Continuous scan + * clients will get a large speed increase by using this instead of decode(). + * + * @param image The pixel data to decode + * @return The contents of the image + * @throws NotFoundException Any errors which occurred + */ + pub fn decode_with_state( + &mut self, + image: &mut BinaryBitmap, + ) -> Result { + // Make sure to set up the default state so we don't crash + if self.possible_formats.is_empty() { + self.set_hints(&HashMap::new()); + } + self.decode_internal(image) + } + + /** + * This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls + * to decodeWithState(image) can reuse the same set of readers without reallocating memory. This + * is important for performance in continuous scan clients. + * + * @param hints The set of hints to use for subsequent calls to decode(image) + */ + pub fn set_hints(&mut self, hints: &DecodingHintDictionary) { + self.hints = hints.clone(); + + self.try_harder = matches!( + self.hints.get(&DecodeHintType::TRY_HARDER), + Some(DecodeHintValue::TryHarder(true)) + ); + self.possible_formats = if let Some(DecodeHintValue::PossibleFormats(formats)) = + hints.get(&DecodeHintType::POSSIBLE_FORMATS) + { + formats.clone() + } else { + HashSet::new() + }; + self.one_d_reader = MultiFormatOneDReader::new(hints); + } + + pub fn decode_internal( + &mut self, + image: &mut BinaryBitmap, + ) -> Result { + let res = self.decode_formats(image); + if res.is_ok() { + return res; + } + if matches!( + self.hints.get(&DecodeHintType::ALSO_INVERTED), + Some(DecodeHintValue::AlsoInverted(true)) + ) { + // Calling all readers again with inverted image + image.get_black_matrix_mut().flip_self(); + let res = self.decode_formats(image); + if res.is_ok() { + return res; + } + } + Err(Exceptions::NOT_FOUND) + } + + fn decode_formats(&mut self, image: &mut BinaryBitmap) -> Result { + if !self.possible_formats.is_empty() { + let one_d = self.possible_formats.contains(&BarcodeFormat::UPC_A) + || self.possible_formats.contains(&BarcodeFormat::UPC_E) + || self.possible_formats.contains(&BarcodeFormat::EAN_13) + || self.possible_formats.contains(&BarcodeFormat::EAN_8) + || self.possible_formats.contains(&BarcodeFormat::CODABAR) + || self.possible_formats.contains(&BarcodeFormat::CODE_39) + || self.possible_formats.contains(&BarcodeFormat::CODE_93) + || self.possible_formats.contains(&BarcodeFormat::CODE_128) + || self.possible_formats.contains(&BarcodeFormat::ITF) + || self.possible_formats.contains(&BarcodeFormat::RSS_14) + || self.possible_formats.contains(&BarcodeFormat::RSS_EXPANDED); + if one_d && !self.try_harder { + if let Ok(res) = self.one_d_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + } + for possible_format in self.possible_formats.iter() { + let res = match possible_format { + BarcodeFormat::QR_CODE => { + self.qr_code_reader.decode_with_hints(image, &self.hints) + } + BarcodeFormat::DATA_MATRIX => { + self.data_matrix_reader.decode_with_hints(image, &self.hints) + } + BarcodeFormat::AZTEC => { + self.aztec_reader.decode_with_hints(image, &self.hints) + } + BarcodeFormat::PDF_417 => { + self.pdf417_reader.decode_with_hints(image, &self.hints) + } + BarcodeFormat::MAXICODE => { + self.maxicode_reader.decode_with_hints(image, &self.hints) + } + _ => Err(Exceptions::UNSUPPORTED_OPERATION), + }; + if res.is_ok() { + return res; + } + } + if one_d && self.try_harder { + if let Ok(res) = self.one_d_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + } + } else { + if !self.try_harder { + if let Ok(res) = self.one_d_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + } + + if let Ok(res) = self.qr_code_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + if let Ok(res) = self.data_matrix_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + if let Ok(res) = self.aztec_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + if let Ok(res) = self.pdf417_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + if let Ok(res) = self.maxicode_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + + if self.try_harder { + if let Ok(res) = self.one_d_reader.decode_with_hints(image, &self.hints) { + return Ok(res); + } + } + } + + Err(Exceptions::UNSUPPORTED_OPERATION) + } + } + \ No newline at end of file