Oops, not clearing row counters when we shift down by two buckets. Fixed.
[zxing.git] / core / src / com / google / zxing / oned / Code128Reader.java
1 /*
2  * Copyright 2008 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.oned;
18
19 import com.google.zxing.ReaderException;
20 import com.google.zxing.Result;
21 import com.google.zxing.ResultPoint;
22 import com.google.zxing.common.BitArray;
23 import com.google.zxing.common.GenericResultPoint;
24
25 /**
26  * <p>Decodes Code 128 barcodes.</p>
27  *
28  * @author srowen@google.com (Sean Owen)
29  */
30 public final class Code128Reader extends AbstractOneDReader {
31
32   private static final int[][] CODE_PATTERNS = {
33       {2, 1, 2, 2, 2, 2}, // 0
34       {2, 2, 2, 1, 2, 2},
35       {2, 2, 2, 2, 2, 1},
36       {1, 2, 1, 2, 2, 3},
37       {1, 2, 1, 3, 2, 2},
38       {1, 3, 1, 2, 2, 2}, // 5
39       {1, 2, 2, 2, 1, 3},
40       {1, 2, 2, 3, 1, 2},
41       {1, 3, 2, 2, 1, 2},
42       {2, 2, 1, 2, 1, 3},
43       {2, 2, 1, 3, 1, 2}, // 10
44       {2, 3, 1, 2, 1, 2},
45       {1, 1, 2, 2, 3, 2},
46       {1, 2, 2, 1, 3, 2},
47       {1, 2, 2, 2, 3, 1},
48       {1, 1, 3, 2, 2, 2}, // 15
49       {1, 2, 3, 1, 2, 2},
50       {1, 2, 3, 2, 2, 1},
51       {2, 2, 3, 2, 1, 1},
52       {2, 2, 1, 1, 3, 2},
53       {2, 2, 1, 2, 3, 1}, // 20
54       {2, 1, 3, 2, 1, 2},
55       {2, 2, 3, 1, 1, 2},
56       {3, 1, 2, 1, 3, 1},
57       {3, 1, 1, 2, 2, 2},
58       {3, 2, 1, 1, 2, 2}, // 25
59       {3, 2, 1, 2, 2, 1},
60       {3, 1, 2, 2, 1, 2},
61       {3, 2, 2, 1, 1, 2},
62       {3, 2, 2, 2, 1, 1},
63       {2, 1, 2, 1, 2, 3}, // 30
64       {2, 1, 2, 3, 2, 1},
65       {2, 3, 2, 1, 2, 1},
66       {1, 1, 1, 3, 2, 3},
67       {1, 3, 1, 1, 2, 3},
68       {1, 3, 1, 3, 2, 1}, // 35
69       {1, 1, 2, 3, 1, 3},
70       {1, 3, 2, 1, 1, 3},
71       {1, 3, 2, 3, 1, 1},
72       {2, 1, 1, 3, 1, 3},
73       {2, 3, 1, 1, 1, 3}, // 40
74       {2, 3, 1, 3, 1, 1},
75       {1, 1, 2, 1, 3, 3},
76       {1, 1, 2, 3, 3, 1},
77       {1, 3, 2, 1, 3, 1},
78       {1, 1, 3, 1, 2, 3}, // 45
79       {1, 1, 3, 3, 2, 1},
80       {1, 3, 3, 1, 2, 1},
81       {3, 1, 3, 1, 2, 1},
82       {2, 1, 1, 3, 3, 1},
83       {2, 3, 1, 1, 3, 1}, // 50
84       {2, 1, 3, 1, 1, 3},
85       {2, 1, 3, 3, 1, 1},
86       {2, 1, 3, 1, 3, 1},
87       {3, 1, 1, 1, 2, 3},
88       {3, 1, 1, 3, 2, 1}, // 55
89       {3, 3, 1, 1, 2, 1},
90       {3, 1, 2, 1, 1, 3},
91       {3, 1, 2, 3, 1, 1},
92       {3, 3, 2, 1, 1, 1},
93       {3, 1, 4, 1, 1, 1}, // 60
94       {2, 2, 1, 4, 1, 1},
95       {4, 3, 1, 1, 1, 1},
96       {1, 1, 1, 2, 2, 4},
97       {1, 1, 1, 4, 2, 2},
98       {1, 2, 1, 1, 2, 4}, // 65
99       {1, 2, 1, 4, 2, 1},
100       {1, 4, 1, 1, 2, 2},
101       {1, 4, 1, 2, 2, 1},
102       {1, 1, 2, 2, 1, 4},
103       {1, 1, 2, 4, 1, 2}, // 70
104       {1, 2, 2, 1, 1, 4},
105       {1, 2, 2, 4, 1, 1},
106       {1, 4, 2, 1, 1, 2},
107       {1, 4, 2, 2, 1, 1},
108       {2, 4, 1, 2, 1, 1}, // 75
109       {2, 2, 1, 1, 1, 4},
110       {4, 1, 3, 1, 1, 1},
111       {2, 4, 1, 1, 1, 2},
112       {1, 3, 4, 1, 1, 1},
113       {1, 1, 1, 2, 4, 2}, // 80
114       {1, 2, 1, 1, 4, 2},
115       {1, 2, 1, 2, 4, 1},
116       {1, 1, 4, 2, 1, 2},
117       {1, 2, 4, 1, 1, 2},
118       {1, 2, 4, 2, 1, 1}, // 85
119       {4, 1, 1, 2, 1, 2},
120       {4, 2, 1, 1, 1, 2},
121       {4, 2, 1, 2, 1, 1},
122       {2, 1, 2, 1, 4, 1},
123       {2, 1, 4, 1, 2, 1}, // 90
124       {4, 1, 2, 1, 2, 1},
125       {1, 1, 1, 1, 4, 3},
126       {1, 1, 1, 3, 4, 1},
127       {1, 3, 1, 1, 4, 1},
128       {1, 1, 4, 1, 1, 3}, // 95
129       {1, 1, 4, 3, 1, 1},
130       {4, 1, 1, 1, 1, 3},
131       {4, 1, 1, 3, 1, 1},
132       {1, 1, 3, 1, 4, 1},
133       {1, 1, 4, 1, 3, 1}, // 100
134       {3, 1, 1, 1, 4, 1},
135       {4, 1, 1, 1, 3, 1},
136       {2, 1, 1, 4, 1, 2},
137       {2, 1, 1, 2, 1, 4},
138       {2, 1, 1, 2, 3, 2}, // 105
139       {2, 3, 3, 1, 1, 1, 2}
140   };
141
142   private static final float MAX_VARIANCE = 0.3f;
143
144   private static final int CODE_SHIFT = 98;
145
146   private static final int CODE_CODE_C = 99;
147   private static final int CODE_CODE_B = 100;
148   private static final int CODE_CODE_A = 101;
149
150   private static final int CODE_FNC_1 = 102;
151   private static final int CODE_FNC_2 = 97;
152   private static final int CODE_FNC_3 = 96;
153   private static final int CODE_FNC_4_A = 101;
154   private static final int CODE_FNC_4_B = 100;
155
156   private static final int CODE_START_A = 103;
157   private static final int CODE_START_B = 104;
158   private static final int CODE_START_C = 105;
159   private static final int CODE_STOP = 106;
160
161   private static int[] findStartPattern(BitArray row) throws ReaderException {
162     int width = row.getSize();
163     int rowOffset = 0;
164     while (rowOffset < width) {
165       if (row.get(rowOffset)) {
166         break;
167       }
168       rowOffset++;
169     }
170
171     int counterPosition = 0;
172     int[] counters = new int[6];
173     int patternStart = rowOffset;
174     boolean isWhite = false;
175     int patternLength = counters.length;
176
177     for (int i = rowOffset; i < width; i++) {
178       boolean pixel = row.get(i);
179       if ((!pixel && isWhite) || (pixel && !isWhite)) {
180         counters[counterPosition]++;
181       } else {
182         if (counterPosition == patternLength - 1) {
183           float bestVariance = MAX_VARIANCE;
184           int bestMatch = -1;
185           for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) {
186             float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode]);
187             if (variance < bestVariance) {
188               bestVariance = variance;
189               bestMatch = startCode;
190             }
191           }
192           if (bestMatch >= 0) {
193             return new int[]{patternStart, i, bestMatch};
194           }
195           patternStart += counters[0] + counters[1];
196           for (int y = 2; y < patternLength; y++) {
197             counters[y - 2] = counters[y];
198           }
199           counters[patternLength - 2] = 0;
200           counters[patternLength - 1] = 0;
201           counterPosition--;
202         } else {
203           counterPosition++;
204         }
205         counters[counterPosition] = 1;
206         isWhite = !isWhite;
207       }
208     }
209     throw new ReaderException("Can't find pattern");
210   }
211
212   private static int decodeCode(BitArray row, int[] counters, int rowOffset) throws ReaderException {
213     recordPattern(row, rowOffset, counters);
214     float bestVariance = MAX_VARIANCE; // worst variance we'll accept
215     int bestMatch = -1;
216     for (int d = 0; d < CODE_PATTERNS.length; d++) {
217       int[] pattern = CODE_PATTERNS[d];
218       float variance = patternMatchVariance(counters, pattern);
219       if (variance < bestVariance) {
220         bestVariance = variance;
221         bestMatch = d;
222       }
223     }
224     // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6
225     if (bestMatch >= 0) {
226       return bestMatch;
227     } else {
228       throw new ReaderException("Could not match any code pattern");
229     }
230   }
231
232   public Result decodeRow(final int rowNumber, final BitArray row) throws ReaderException {
233
234     int[] startPatternInfo = findStartPattern(row);
235     int startCode = startPatternInfo[2];
236     int codeSet;
237     switch (startCode) {
238       case CODE_START_A:
239         codeSet = CODE_CODE_A;
240         break;
241       case CODE_START_B:
242         codeSet = CODE_CODE_B;
243         break;
244       case CODE_START_C:
245         codeSet = CODE_CODE_C;
246         break;
247       default:
248         throw new ReaderException("Illegal start code");
249     }
250
251     boolean done = false;
252     boolean isNextShifted = false;
253
254     StringBuffer result = new StringBuffer();
255     int lastStart = startPatternInfo[0];
256     int nextStart = startPatternInfo[1];
257     int[] counters = new int[6];
258
259     int lastCode = 0;
260     int code = 0;
261     int checksumTotal = startCode;
262     int multiplier = 0;
263
264     while (!done) {
265
266       boolean unshift = isNextShifted;
267       isNextShifted = false;
268
269       lastCode = code;
270       code = decodeCode(row, counters, nextStart);
271       if (code != CODE_STOP) {
272         multiplier++;
273         checksumTotal += multiplier * code;
274       }
275
276       lastStart = nextStart;
277       for (int i = 0; i < counters.length; i++) {
278         nextStart += counters[i];
279       }
280
281       // Take care of illegal start codes
282       switch (code) {
283         case CODE_START_A:
284         case CODE_START_B:
285         case CODE_START_C:
286           throw new ReaderException("Unexpected start code");
287       }
288
289       switch (codeSet) {
290
291         case CODE_CODE_A:
292           if (code < 64) {
293             result.append((char) (' ' + code));
294           } else if (code < 96) {
295             result.append((char) (code - 64));
296           } else {
297             switch (code) {
298               case CODE_FNC_1:
299               case CODE_FNC_2:
300               case CODE_FNC_3:
301               case CODE_FNC_4_A:
302                 // do nothing?
303                 break;
304               case CODE_SHIFT:
305                 isNextShifted = true;
306                 codeSet = CODE_CODE_B;
307                 break;
308               case CODE_CODE_B:
309                 codeSet = CODE_CODE_B;
310                 break;
311               case CODE_CODE_C:
312                 codeSet = CODE_CODE_C;
313                 break;
314               case CODE_STOP:
315                 done = true;
316                 break;
317             }
318           }
319           break;
320         case CODE_CODE_B:
321           if (code < 96) {
322             result.append((char) (' ' + code));
323           } else {
324             switch (code) {
325               case CODE_FNC_1:
326               case CODE_FNC_2:
327               case CODE_FNC_3:
328               case CODE_FNC_4_B:
329                 // do nothing?
330                 break;
331               case CODE_SHIFT:
332                 isNextShifted = true;
333                 codeSet = CODE_CODE_C;
334                 break;
335               case CODE_CODE_A:
336                 codeSet = CODE_CODE_A;
337                 break;
338               case CODE_CODE_C:
339                 codeSet = CODE_CODE_C;
340                 break;
341               case CODE_STOP:
342                 done = true;
343                 break;
344             }
345           }
346           break;
347         case CODE_CODE_C:
348           if (code < 100) {
349             if (code < 10) {
350               result.append('0');
351             }
352             result.append(code);
353           } else {
354             switch (code) {
355               case CODE_FNC_1:
356                 // do nothing?
357                 break;
358               case CODE_CODE_A:
359                 codeSet = CODE_CODE_A;
360                 break;
361               case CODE_CODE_B:
362                 codeSet = CODE_CODE_B;
363                 break;
364               case CODE_STOP:
365                 done = true;
366                 break;
367             }
368           }
369           break;
370       }
371
372       if (unshift) {
373         switch (codeSet) {
374           case CODE_CODE_A:
375             codeSet = CODE_CODE_C;
376             break;
377           case CODE_CODE_B:
378             codeSet = CODE_CODE_A;
379             break;
380           case CODE_CODE_C:
381             codeSet = CODE_CODE_B;
382             break;
383         }
384       }
385
386     }
387
388     // Pull out from sum the value of the penultimate check code
389     checksumTotal -= multiplier * lastCode;
390     if (checksumTotal % 103 != lastCode) {
391       throw new ReaderException("Checksum failed");
392     }
393
394     // Need to pull out the check digits from string
395     int resultLength = result.length();
396     if (resultLength > 0) {
397       if (codeSet == CODE_CODE_C) {
398         result.delete(resultLength - 2, resultLength);
399       } else {
400         result.delete(resultLength - 1, resultLength);
401       }
402     }
403
404     String resultString = result.toString();
405     return new Result(resultString,
406         new ResultPoint[]{new GenericResultPoint((float) (startPatternInfo[1] - startPatternInfo[0]) / 2.0f,
407             (float) rowNumber),
408             new GenericResultPoint((float) (nextStart - lastStart) / 2.0f,
409                 (float) rowNumber)});
410
411   }
412
413 }