Skip to main content

rxing/qrcode/
qr_code_reader.rs

1/*
2 * Copyright 2007 ZXing authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use 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/**
30 * This implementation can detect and decode QR Codes in an image.
31 *
32 * @author Sean Owen
33 */
34#[derive(Default)]
35pub struct QRCodeReader;
36// pub struct QRCodeReader;  {
37
38//   // private static final Point[] NO_POINTS = new Point[0];
39// }
40
41impl Reader for QRCodeReader {
42    /**
43     * Locates and decodes a QR code in an image.
44     *
45     * @return a String representing the content encoded by the QR code
46     * @throws NotFoundException if a QR code cannot be found
47     * @throws FormatException if a QR code cannot be decoded
48     * @throws ChecksumException if error correction fails
49     */
50    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    /**
79     * This method detects a code in a "pure" image -- that is, pure monochrome image
80     * which contains only an unrotated, unskewed, image of a code, with some white border
81     * around it. This is a specialized method that works exceptionally fast in this special
82     * case.
83     */
84    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        // Sanity check!
102        if left >= right || top >= bottom {
103            return Err(Exceptions::NOT_FOUND);
104        }
105
106        if bottom - top != right - left {
107            // Special case, where bottom-right module wasn't black so we found something else in the last row
108            // Assume it's a square, so use height as the width
109            right = left + (bottom - top);
110            if right >= image.getWidth() as i32 {
111                // Abort if that would not make sense -- off image
112                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            // Only possibly decode square regions
122            return Err(Exceptions::NOT_FOUND);
123        }
124
125        // Push in the "border" by half the module width so that we start
126        // sampling in the middle of the module. Just in case the image is a
127        // little off, this will help recover.
128        let nudge = (moduleSize / 2.0) as u32;
129        top += nudge as i32;
130        left += nudge as i32;
131
132        // But careful that this does not sample off the edge
133        // "right" is the farthest-right valid pixel location -- right+1 is not necessarily
134        // This is positive by how much the inner x loop below would be too large
135        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                // Neither way fits; abort
140                return Err(Exceptions::NOT_FOUND);
141            }
142            left -= nudgedTooFarRight;
143        }
144        // See logic above
145        let nudgedTooFarDown = top + ((matrixHeight - 1) as f32 * moduleSize) as i32 - bottom;
146        if nudgedTooFarDown > 0 {
147            if nudgedTooFarDown > nudge as i32 {
148                // Neither way fits; abort
149                return Err(Exceptions::NOT_FOUND);
150            }
151            top -= nudgedTooFarDown;
152        }
153
154        // Now just read off the bits
155        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 the code was mirrored: swap the bottom-left and the top-right points.
211        if let Some(other) = decoderRXingResult.getOther() {
212            if other.is::<QRCodeDecoderMetaData>() {
213                // if (decoderRXingResult.getOther() instanceof QRCodeDecoderMetaData) {
214                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}