]> Pileus Git - ~andy/freeotp/blob - src/com/google/zxing/qrcode/QRCodeReader.java
Add native camera support
[~andy/freeotp] / src / com / google / zxing / qrcode / QRCodeReader.java
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
17 package com.google.zxing.qrcode;
18
19 import com.google.zxing.BarcodeFormat;
20 import com.google.zxing.BinaryBitmap;
21 import com.google.zxing.ChecksumException;
22 import com.google.zxing.DecodeHintType;
23 import com.google.zxing.FormatException;
24 import com.google.zxing.NotFoundException;
25 import com.google.zxing.Reader;
26 import com.google.zxing.Result;
27 import com.google.zxing.ResultMetadataType;
28 import com.google.zxing.ResultPoint;
29 import com.google.zxing.common.BitMatrix;
30 import com.google.zxing.common.DecoderResult;
31 import com.google.zxing.common.DetectorResult;
32 import com.google.zxing.qrcode.decoder.Decoder;
33 import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
34 import com.google.zxing.qrcode.detector.Detector;
35
36 import java.util.List;
37 import java.util.Map;
38
39 /**
40  * This implementation can detect and decode QR Codes in an image.
41  *
42  * @author Sean Owen
43  */
44 public class QRCodeReader implements Reader {
45
46   private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
47
48   private final Decoder decoder = new Decoder();
49
50   protected final Decoder getDecoder() {
51     return decoder;
52   }
53
54   /**
55    * Locates and decodes a QR code in an image.
56    *
57    * @return a String representing the content encoded by the QR code
58    * @throws NotFoundException if a QR code cannot be found
59    * @throws FormatException if a QR code cannot be decoded
60    * @throws ChecksumException if error correction fails
61    */
62   @Override
63   public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
64     return decode(image, null);
65   }
66
67   @Override
68   public final Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
69       throws NotFoundException, ChecksumException, FormatException {
70     DecoderResult decoderResult;
71     ResultPoint[] points;
72     if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
73       BitMatrix bits = extractPureBits(image.getBlackMatrix());
74       decoderResult = decoder.decode(bits, hints);
75       points = NO_POINTS;
76     } else {
77       DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints);
78       decoderResult = decoder.decode(detectorResult.getBits(), hints);
79       points = detectorResult.getPoints();
80     }
81
82     // If the code was mirrored: swap the bottom-left and the top-right points.
83     if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
84       ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
85     }
86
87     Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE);
88     List<byte[]> byteSegments = decoderResult.getByteSegments();
89     if (byteSegments != null) {
90       result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
91     }
92     String ecLevel = decoderResult.getECLevel();
93     if (ecLevel != null) {
94       result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
95     }
96     return result;
97   }
98
99   @Override
100   public void reset() {
101     // do nothing
102   }
103
104   /**
105    * This method detects a code in a "pure" image -- that is, pure monochrome image
106    * which contains only an unrotated, unskewed, image of a code, with some white border
107    * around it. This is a specialized method that works exceptionally fast in this special
108    * case.
109    *
110    * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
111    */
112   private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException {
113
114     int[] leftTopBlack = image.getTopLeftOnBit();
115     int[] rightBottomBlack = image.getBottomRightOnBit();
116     if (leftTopBlack == null || rightBottomBlack == null) {
117       throw NotFoundException.getNotFoundInstance();
118     }
119
120     float moduleSize = moduleSize(leftTopBlack, image);
121
122     int top = leftTopBlack[1];
123     int bottom = rightBottomBlack[1];
124     int left = leftTopBlack[0];
125     int right = rightBottomBlack[0];
126     
127     // Sanity check!
128     if (left >= right || top >= bottom) {
129       throw NotFoundException.getNotFoundInstance();
130     }
131
132     if (bottom - top != right - left) {
133       // Special case, where bottom-right module wasn't black so we found something else in the last row
134       // Assume it's a square, so use height as the width
135       right = left + (bottom - top);
136     }
137
138     int matrixWidth = Math.round((right - left + 1) / moduleSize);
139     int matrixHeight = Math.round((bottom - top + 1) / moduleSize);
140     if (matrixWidth <= 0 || matrixHeight <= 0) {
141       throw NotFoundException.getNotFoundInstance();
142     }
143     if (matrixHeight != matrixWidth) {
144       // Only possibly decode square regions
145       throw NotFoundException.getNotFoundInstance();
146     }
147
148     // Push in the "border" by half the module width so that we start
149     // sampling in the middle of the module. Just in case the image is a
150     // little off, this will help recover.
151     int nudge = (int) (moduleSize / 2.0f);
152     top += nudge;
153     left += nudge;
154     
155     // But careful that this does not sample off the edge
156     int nudgedTooFarRight = left + (int) ((matrixWidth - 1) * moduleSize) - (right - 1);
157     if (nudgedTooFarRight > 0) {
158       if (nudgedTooFarRight > nudge) {
159         // Neither way fits; abort
160         throw NotFoundException.getNotFoundInstance();
161       }
162       left -= nudgedTooFarRight;
163     }
164     int nudgedTooFarDown = top + (int) ((matrixHeight - 1) * moduleSize) - (bottom - 1);
165     if (nudgedTooFarDown > 0) {
166       if (nudgedTooFarDown > nudge) {
167         // Neither way fits; abort
168         throw NotFoundException.getNotFoundInstance();
169       }
170       top -= nudgedTooFarDown;
171     }
172
173     // Now just read off the bits
174     BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight);
175     for (int y = 0; y < matrixHeight; y++) {
176       int iOffset = top + (int) (y * moduleSize);
177       for (int x = 0; x < matrixWidth; x++) {
178         if (image.get(left + (int) (x * moduleSize), iOffset)) {
179           bits.set(x, y);
180         }
181       }
182     }
183     return bits;
184   }
185
186   private static float moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException {
187     int height = image.getHeight();
188     int width = image.getWidth();
189     int x = leftTopBlack[0];
190     int y = leftTopBlack[1];
191     boolean inBlack = true;
192     int transitions = 0;
193     while (x < width && y < height) {
194       if (inBlack != image.get(x, y)) {
195         if (++transitions == 5) {
196           break;
197         }
198         inBlack = !inBlack;
199       }
200       x++;
201       y++;
202     }
203     if (x == width || y == height) {
204       throw NotFoundException.getNotFoundInstance();
205     }
206     return (x - leftTopBlack[0]) / 7.0f;
207   }
208
209 }