Refactored Reed-Solomon so it can be used with different GF(256) primitive polynomials
[zxing.git] / core / src / com / google / zxing / qrcode / QRCodeReader.java
1 /*
2  * Copyright 2007 Google Inc.
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.DecodeHintType;
20 import com.google.zxing.MonochromeBitmapSource;
21 import com.google.zxing.Reader;
22 import com.google.zxing.ReaderException;
23 import com.google.zxing.Result;
24 import com.google.zxing.ResultPoint;
25 import com.google.zxing.common.BitMatrix;
26 import com.google.zxing.qrcode.decoder.Decoder;
27 import com.google.zxing.qrcode.detector.Detector;
28 import com.google.zxing.qrcode.detector.DetectorResult;
29
30 import java.util.Hashtable;
31
32 /**
33  * This implementation can detect and decode QR Codes in an image.
34  *
35  * @author srowen@google.com (Sean Owen)
36  */
37 public final class QRCodeReader implements Reader {
38
39   private static final ResultPoint[] NO_POINTS = new ResultPoint[0];
40
41   private final Decoder decoder = new Decoder();
42
43   /**
44    * Locates and decodes a QR code in an image.
45    *
46    * @return a String representing the content encoded by the QR code
47    * @throws ReaderException if a QR code cannot be found, or cannot be decoded
48    */
49   public Result decode(MonochromeBitmapSource image) throws ReaderException {
50     return decode(image, null);
51   }
52
53   public Result decode(MonochromeBitmapSource image, Hashtable hints)
54       throws ReaderException {
55     String text;
56     ResultPoint[] points;
57     if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) {
58       BitMatrix bits = extractPureBits(image);
59       text = decoder.decode(bits);
60       points = NO_POINTS;
61     } else {
62       DetectorResult result = new Detector(image).detect();
63       text = decoder.decode(result.getBits());
64       points = result.getPoints();
65     }
66     return new Result(text, points);
67   }
68
69   /**
70    * This method detects a barcode in a "pure" image -- that is, pure monochrome image
71    * which contains only an unrotated, unskewed, image of a barcode, with some white border
72    * around it. This is a specialized method that works exceptionally fast in this special
73    * case.
74    */
75   private static BitMatrix extractPureBits(MonochromeBitmapSource image)
76       throws ReaderException {
77     // Now need to determine module size in pixels
78
79     // First, skip white border by tracking diagonally from the top left down and to the right:
80     int borderWidth = 0;
81     while (!image.isBlack(borderWidth, borderWidth)) {
82       borderWidth++;
83     }
84     // And then keep tracking across the top-left black module to determine module size
85     int moduleEnd = borderWidth;
86     while (image.isBlack(moduleEnd, moduleEnd)) {
87       moduleEnd++;
88     }
89     int moduleSize = moduleEnd - borderWidth;
90
91     // And now find where the rightmost black module on the first row ends
92     int rowEndOfSymbol = image.getWidth() - 1;
93     while (!image.isBlack(rowEndOfSymbol, borderWidth)) {
94       rowEndOfSymbol--;
95     }
96     rowEndOfSymbol++;
97
98     // Make sure width of barcode is a multiple of module size
99     if ((rowEndOfSymbol - borderWidth) % moduleSize != 0) {
100       throw new ReaderException("Bad module size / width: " + moduleSize +
101           " / " + (rowEndOfSymbol - borderWidth));
102     }
103     int dimension = (rowEndOfSymbol - borderWidth) / moduleSize;
104
105     // Push in the "border" by half the module width so that we start
106     // sampling in the middle of the module. Just in case the image is a
107     // little off, this will help recover.
108     borderWidth += moduleSize >> 1;
109
110     // Now just read off the bits
111     BitMatrix bits = new BitMatrix(dimension);
112     for (int i = 0; i < dimension; i++) {
113       int iOffset = borderWidth + i * moduleSize;
114       for (int j = 0; j < dimension; j++) {
115         if (image.isBlack(borderWidth + j * moduleSize, iOffset)) {
116           bits.set(i, j);
117         }
118       }
119     }
120     return bits;
121   }
122
123 }