0ab71b70490398d869d30bfae0bc10b31439d66b
[zxing.git] / cpp / core / src / zxing / oned / OneDReader.cpp
1 /*
2  *  OneDReader.cpp
3  *  ZXing
4  *
5  *  Copyright 2010 ZXing authors All rights reserved.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include "OneDReader.h"
21 #include <zxing/ReaderException.h>
22 #include <zxing/oned/OneDResultPoint.h>
23 #include <math.h>
24 #include <limits.h>
25
26 namespace zxing {
27         namespace oned {
28                 using namespace std;
29
30                 OneDReader::OneDReader() {
31                 }
32
33                 Ref<Result> OneDReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
34                   try {
35                                 return doDecode(image, hints);
36                         } catch (ReaderException re) {
37                                 if (hints.getTryHarder() && image->isRotateSupported()) {
38                                         Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise());
39                                         Ref<Result> result(doDecode(rotatedImage, hints));
40                                         /*
41                                         // Record that we found it rotated 90 degrees CCW / 270 degrees CW
42                                         Hashtable metadata = result.getResultMetadata();
43                                         int orientation = 270;
44                                         if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {
45                                                 // But if we found it reversed in doDecode(), add in that result here:
46                                                 orientation = (orientation +
47                                                                            ((Integer) metadata.get(ResultMetadataType.ORIENTATION)).intValue()) % 360;
48                                         }
49                                         result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation));
50                                         */
51           // Update result points
52                                         std::vector<Ref<ResultPoint> > points (result->getResultPoints());
53                                         int height = rotatedImage->getHeight();
54                                         for (size_t i = 0; i < points.size(); i++) {
55                                                 points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX()));
56                                         }
57                                         return result;
58                                 } else {
59                                         throw re;
60                                 }
61                         }
62                 }
63
64                 Ref<Result> OneDReader::doDecode(Ref<BinaryBitmap> image, DecodeHints hints) {
65                         int width = image->getWidth();
66                         int height = image->getHeight();
67                         Ref<BitArray> row(new BitArray(width));
68                         int middle = height >> 1;
69                         bool tryHarder = hints.getTryHarder();
70                         int rowStep = (int)fmax(1, height >> (tryHarder ? 8 : 5));
71                         int maxLines;
72                         if (tryHarder) {
73                                 maxLines = height; // Look at the whole image, not just the center
74                         } else {
75         maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image
76                         }
77
78                         for (int x = 0; x < maxLines; x++) {
79                                 // Scanning from the middle out. Determine which row we're looking at next:
80                                 int rowStepsAboveOrBelow = (x + 1) >> 1;
81                                 bool isAbove = (x & 0x01) == 0; // i.e. is x even?
82                                 int rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow);
83                                 if (rowNumber < 0 || rowNumber >= height) {
84                                         // Oops, if we run off the top or bottom, stop
85                                         break;
86                                 }
87
88                                 // Estimate black point for this row and load it:
89                                 try {
90                                         row = image->getBlackRow(rowNumber, row);
91                                 } catch (ReaderException re) {
92                                         continue;
93                                 } catch (IllegalArgumentException re) {
94           continue;
95                                 }
96
97                                 // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to
98                                 // handle decoding upside down barcodes.
99                                 for (int attempt = 0; attempt < 2; attempt++) {
100                                         if (attempt == 1) { // trying again?
101                                                 row->reverse(); // reverse the row and continue
102                                         }
103
104           // Look for a barcode
105           Ref<Result> result = decodeRow(rowNumber, row);
106           // We found our barcode
107           if (!result.empty()) {
108             //                                          // But it was upside down, so note that
109             //                                          result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(180));
110             //                                          // And remember to flip the result points horizontally.
111             std::vector<Ref<ResultPoint> > points(result->getResultPoints());
112             // if there's exactly two points (which there should be), flip the x coordinate
113             // if there's not exactly 2, I don't know what do do with it
114             if (points.size() == 2) {
115               Ref<ResultPoint> pointZero(new OneDResultPoint(width - points[0]->getX() - 1, points[0]->getY()));
116               points[0] = pointZero;
117
118               Ref<ResultPoint> pointOne(new OneDResultPoint(width - points[1]->getX() - 1, points[1]->getY()));
119               points[1] = pointOne;
120
121               result.reset(new Result(result->getText(),result->getRawBytes(),points,result->getBarcodeFormat()));
122             }
123             return result;
124           }
125                                 }
126                         }
127                         throw ReaderException("doDecode() failed");
128                 }
129
130                 unsigned int OneDReader::patternMatchVariance(int counters[], int countersSize, const int pattern[], int maxIndividualVariance) {
131                         int numCounters = countersSize;
132                         unsigned int total = 0;
133                         unsigned int patternLength = 0;
134                         for (int i = 0; i < numCounters; i++) {
135                                 total += counters[i];
136                                 patternLength += pattern[i];
137                         }
138                         if (total < patternLength) {
139                                 // If we don't even have one pixel per unit of bar width, assume this is too small
140                                 // to reliably match, so fail:
141                                 return INT_MAX;
142                         }
143                         // We're going to fake floating-point math in integers. We just need to use more bits.
144                         // Scale up patternLength so that intermediate values below like scaledCounter will have
145                         // more "significant digits"
146                         unsigned int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
147                         maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
148
149                         unsigned int totalVariance = 0;
150                         for (int x = 0; x < numCounters; x++) {
151                                 int counter = counters[x] << INTEGER_MATH_SHIFT;
152                                 int scaledPattern = pattern[x] * unitBarWidth;
153                                 int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
154                                 if (variance > maxIndividualVariance) {
155                                         return INT_MAX;
156                                 }
157                                 totalVariance += variance;
158                         }
159                         return totalVariance / total;
160                 }
161
162                 void OneDReader::recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount){
163                         int numCounters = countersCount;//sizeof(counters) / sizeof(int);
164                         for (int i = 0; i < numCounters; i++) {
165                                 counters[i] = 0;
166                         }
167                         int end = row->getSize();
168                         if (start >= end) {
169                                 throw ReaderException("recordPattern: start >= end");
170                         }
171                         bool isWhite = !row->get(start);
172                         int counterPosition = 0;
173                         int i = start;
174                         while (i < end) {
175                                 bool pixel = row->get(i);
176                                 if (pixel ^ isWhite) { // that is, exactly one is true
177                                         counters[counterPosition]++;
178                                 } else {
179                                         counterPosition++;
180                                         if (counterPosition == numCounters) {
181                                                 break;
182                                         } else {
183                                                 counters[counterPosition] = 1;
184                                                 isWhite ^= true; // isWhite = !isWhite;
185                                         }
186                                 }
187                                 i++;
188                         }
189                         // If we read fully the last section of pixels and filled up our counters -- or filled
190                         // the last counter but ran off the side of the image, OK. Otherwise, a problem.
191                         if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
192                                 throw ReaderException("recordPattern");
193                         }
194                 }
195
196                 OneDReader::~OneDReader() {
197                 }
198         }
199 }