Removed as many exceptions as possible from the C++ product readers
[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                   Ref<Result> result = doDecode(image, hints);
35                   if (result.empty() && hints.getTryHarder() && image->isRotateSupported()) {
36                     Ref<BinaryBitmap> rotatedImage(image->rotateCounterClockwise());
37                                 result = doDecode(rotatedImage, hints);
38                                 if (!result.empty()) {
39                                   /*
40                                         // Record that we found it rotated 90 degrees CCW / 270 degrees CW
41                                         Hashtable metadata = result.getResultMetadata();
42                                         int orientation = 270;
43                                         if (metadata != null && metadata.containsKey(ResultMetadataType.ORIENTATION)) {
44                                                 // But if we found it reversed in doDecode(), add in that result here:
45                                                 orientation = (orientation +
46                                                                            ((Integer) metadata.get(ResultMetadataType.ORIENTATION)).intValue()) % 360;
47                                         }
48                                         result.putMetadata(ResultMetadataType.ORIENTATION, new Integer(orientation));
49                                         */
50           // Update result points
51                                         std::vector<Ref<ResultPoint> > points (result->getResultPoints());
52                                         int height = rotatedImage->getHeight();
53                                         for (size_t i = 0; i < points.size(); i++) {
54                                                 points[i].reset(new OneDResultPoint(height - points[i]->getY() - 1, points[i]->getX()));
55                                         }
56                                 }
57                   }
58                   if (result.empty()) {
59                     throw ReaderException("");
60                   }
61                   return result;
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,
116                   points[0]->getY()));
117               points[0] = pointZero;
118
119               Ref<ResultPoint> pointOne(new OneDResultPoint(width - points[1]->getX() - 1,
120                   points[1]->getY()));
121               points[1] = pointOne;
122
123               result.reset(new Result(result->getText(), result->getRawBytes(), points,
124                   result->getBarcodeFormat()));
125             }
126             return result;
127           }
128                                 }
129                         }
130                         return Ref<Result>();
131                 }
132
133                 unsigned int OneDReader::patternMatchVariance(int counters[], int countersSize,
134                     const int pattern[], int maxIndividualVariance) {
135                         int numCounters = countersSize;
136                         unsigned int total = 0;
137                         unsigned int patternLength = 0;
138                         for (int i = 0; i < numCounters; i++) {
139                                 total += counters[i];
140                                 patternLength += pattern[i];
141                         }
142                         if (total < patternLength) {
143                                 // If we don't even have one pixel per unit of bar width, assume this is too small
144                                 // to reliably match, so fail:
145                                 return INT_MAX;
146                         }
147                         // We're going to fake floating-point math in integers. We just need to use more bits.
148                         // Scale up patternLength so that intermediate values below like scaledCounter will have
149                         // more "significant digits"
150                         unsigned int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
151                         maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
152
153                         unsigned int totalVariance = 0;
154                         for (int x = 0; x < numCounters; x++) {
155                                 int counter = counters[x] << INTEGER_MATH_SHIFT;
156                                 int scaledPattern = pattern[x] * unitBarWidth;
157                                 int variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter;
158                                 if (variance > maxIndividualVariance) {
159                                         return INT_MAX;
160                                 }
161                                 totalVariance += variance;
162                         }
163                         return totalVariance / total;
164                 }
165
166                 bool OneDReader::recordPattern(Ref<BitArray> row, int start, int counters[], int countersCount) {
167                         int numCounters = countersCount;//sizeof(counters) / sizeof(int);
168                         for (int i = 0; i < numCounters; i++) {
169                                 counters[i] = 0;
170                         }
171                         int end = row->getSize();
172                         if (start >= end) {
173                                 return false;
174                         }
175                         bool isWhite = !row->get(start);
176                         int counterPosition = 0;
177                         int i = start;
178                         while (i < end) {
179                                 bool pixel = row->get(i);
180                                 if (pixel ^ isWhite) { // that is, exactly one is true
181                                         counters[counterPosition]++;
182                                 } else {
183                                         counterPosition++;
184                                         if (counterPosition == numCounters) {
185                                                 break;
186                                         } else {
187                                                 counters[counterPosition] = 1;
188                                                 isWhite ^= true; // isWhite = !isWhite;
189                                         }
190                                 }
191                                 i++;
192                         }
193                         // If we read fully the last section of pixels and filled up our counters -- or filled
194                         // the last counter but ran off the side of the image, OK. Otherwise, a problem.
195                         if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
196                                 return false;
197                         }
198                         return true;
199                 }
200
201                 OneDReader::~OneDReader() {
202                 }
203         }
204 }