rxing/qrcode/
qr_code_reader.rs1use crate::{
18 BarcodeFormat, Binarizer, DecodeHints, Exceptions, ImmutableReader, Point, RXingResult,
19 RXingResultMetadataType, RXingResultMetadataValue, Reader,
20 common::{BitMatrix, DecoderRXingResult, DetectorRXingResult, Result},
21 point,
22};
23
24use super::{
25 decoder::{QRCodeDecoderMetaData, qrcode_decoder},
26 detector::Detector,
27};
28
29#[derive(Default)]
35pub struct QRCodeReader;
36impl Reader for QRCodeReader {
42 fn decode<B: Binarizer>(&mut self, image: &mut crate::BinaryBitmap<B>) -> Result<RXingResult> {
51 self.decode_with_hints(image, &DecodeHints::default())
52 }
53
54 fn decode_with_hints<B: Binarizer>(
55 &mut self,
56 image: &mut crate::BinaryBitmap<B>,
57 hints: &DecodeHints,
58 ) -> Result<RXingResult> {
59 self.internal_decode_with_hints(image, hints)
60 }
61}
62
63impl ImmutableReader for QRCodeReader {
64 fn immutable_decode_with_hints<B: Binarizer>(
65 &self,
66 image: &mut crate::BinaryBitmap<B>,
67 hints: &DecodeHints,
68 ) -> Result<RXingResult> {
69 self.internal_decode_with_hints(image, hints)
70 }
71}
72
73impl QRCodeReader {
74 pub fn new() -> Self {
75 Self {}
76 }
77
78 fn extractPureBits(image: &BitMatrix) -> Result<BitMatrix> {
85 let leftTopBlack = image.getTopLeftOnBit();
86 let rightBottomBlack = image.getBottomRightOnBit();
87 if leftTopBlack.is_none() || rightBottomBlack.is_none() {
88 return Err(Exceptions::NOT_FOUND);
89 }
90
91 let leftTopBlack = leftTopBlack.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?;
92 let rightBottomBlack = rightBottomBlack.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?;
93
94 let moduleSize = Self::moduleSize(leftTopBlack, image)?;
95
96 let mut top = leftTopBlack.y as i32;
97 let bottom = rightBottomBlack.y as i32;
98 let mut left = leftTopBlack.x as i32;
99 let mut right = rightBottomBlack.x as i32;
100
101 if left >= right || top >= bottom {
103 return Err(Exceptions::NOT_FOUND);
104 }
105
106 if bottom - top != right - left {
107 right = left + (bottom - top);
110 if right >= image.getWidth() as i32 {
111 return Err(Exceptions::NOT_FOUND);
113 }
114 }
115 let matrixWidth = ((right as f32 - left as f32 + 1.0) / moduleSize).round() as u32;
116 let matrixHeight = ((bottom as f32 - top as f32 + 1.0) / moduleSize).round() as u32;
117 if matrixWidth == 0 || matrixHeight == 0 {
118 return Err(Exceptions::NOT_FOUND);
119 }
120 if matrixHeight != matrixWidth {
121 return Err(Exceptions::NOT_FOUND);
123 }
124
125 let nudge = (moduleSize / 2.0) as u32;
129 top += nudge as i32;
130 left += nudge as i32;
131
132 let nudgedTooFarRight =
136 left + ((matrixWidth as i32 - 1) as f32 * moduleSize) as i32 - right;
137 if nudgedTooFarRight > 0 {
138 if nudgedTooFarRight > nudge as i32 {
139 return Err(Exceptions::NOT_FOUND);
141 }
142 left -= nudgedTooFarRight;
143 }
144 let nudgedTooFarDown = top + ((matrixHeight - 1) as f32 * moduleSize) as i32 - bottom;
146 if nudgedTooFarDown > 0 {
147 if nudgedTooFarDown > nudge as i32 {
148 return Err(Exceptions::NOT_FOUND);
150 }
151 top -= nudgedTooFarDown;
152 }
153
154 let mut bits = BitMatrix::new(matrixWidth, matrixHeight)?;
156 for y in 0..matrixHeight {
157 let iOffset = top + ((y as f32) * moduleSize) as i32;
158 for x in 0..matrixWidth {
159 if image.get(left as u32 + (x as f32 * moduleSize) as u32, iOffset as u32) {
160 bits.set(x, y);
161 }
162 }
163 }
164 Ok(bits)
165 }
166
167 fn moduleSize(leftTopBlack: Point, image: &BitMatrix) -> Result<f32> {
168 let height = image.getHeight() as f32;
169 let width = image.getWidth() as f32;
170 let mut x = leftTopBlack.x;
171 let mut y = leftTopBlack.y;
172 let mut inBlack = true;
173 let mut transitions = 0;
174 while x < width && y < height {
175 if inBlack != image.get_point(point(x, y)) {
176 transitions += 1;
177 if transitions == 5 {
178 break;
179 }
180 inBlack = !inBlack;
181 }
182 x += 1.0;
183 y += 1.0;
184 }
185 if x == width || y == height {
186 return Err(Exceptions::NOT_FOUND);
187 }
188 Ok((x - leftTopBlack.x) / 7.0)
189 }
190
191 fn internal_decode_with_hints<B: Binarizer>(
192 &self,
193 image: &mut crate::BinaryBitmap<B>,
194 hints: &DecodeHints,
195 ) -> Result<RXingResult> {
196 let decoderRXingResult: DecoderRXingResult;
197 let mut points: Vec<Point>;
198 if matches!(hints.PureBarcode, Some(true)) {
199 let bits = Self::extractPureBits(image.get_black_matrix())?;
200 decoderRXingResult = qrcode_decoder::decode_bitmatrix_with_hints(&bits, hints)?;
201 points = Vec::new();
202 } else {
203 let detectorRXingResult =
204 Detector::new(image.get_black_matrix()).detect_with_hints(hints)?;
205 decoderRXingResult =
206 qrcode_decoder::decode_bitmatrix_with_hints(detectorRXingResult.getBits(), hints)?;
207 points = detectorRXingResult.getPoints().to_vec();
208 }
209
210 if let Some(other) = decoderRXingResult.getOther() {
212 if other.is::<QRCodeDecoderMetaData>() {
213 other
215 .downcast_ref::<QRCodeDecoderMetaData>()
216 .ok_or(Exceptions::ILLEGAL_STATE)?
217 .applyMirroredCorrection(&mut points);
218 }
219 }
220
221 let mut result = RXingResult::new(
222 decoderRXingResult.getText(),
223 decoderRXingResult.getRawBytes().clone(),
224 points,
225 BarcodeFormat::QR_CODE,
226 );
227
228 let byteSegments = decoderRXingResult.getByteSegments();
229 if !byteSegments.is_empty() {
230 result.putMetadata(
231 RXingResultMetadataType::BYTE_SEGMENTS,
232 RXingResultMetadataValue::ByteSegments(byteSegments.clone()),
233 );
234 }
235
236 let ecLevel = decoderRXingResult.getECLevel();
237 if !ecLevel.is_empty() {
238 result.putMetadata(
239 RXingResultMetadataType::ERROR_CORRECTION_LEVEL,
240 RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned()),
241 );
242 }
243
244 if decoderRXingResult.hasStructuredAppend() {
245 result.putMetadata(
246 RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE,
247 RXingResultMetadataValue::StructuredAppendSequence(
248 decoderRXingResult.getStructuredAppendSequenceNumber(),
249 ),
250 );
251 result.putMetadata(
252 RXingResultMetadataType::STRUCTURED_APPEND_PARITY,
253 RXingResultMetadataValue::StructuredAppendParity(
254 decoderRXingResult.getStructuredAppendParity(),
255 ),
256 );
257 }
258
259 result.putMetadata(
260 RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
261 RXingResultMetadataValue::SymbologyIdentifier(format!(
262 "]Q{}",
263 decoderRXingResult.getSymbologyModifier()
264 )),
265 );
266
267 Ok(result)
268 }
269}