Added some comments and fixed up lines over 100 columns.
[zxing.git] / core / src / com / google / zxing / oned / AbstractUPCEANReader.java
index 707ee62..ee39d08 100644 (file)
 package com.google.zxing.oned;
 
 import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
 import com.google.zxing.ReaderException;
 import com.google.zxing.Result;
 import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
 import com.google.zxing.common.BitArray;
-import com.google.zxing.common.GenericResultPoint;
 
 import java.util.Hashtable;
 
@@ -30,18 +31,21 @@ import java.util.Hashtable;
  * of one-dimensional barcodes.</p>
  *
  * @author dswitkin@google.com (Daniel Switkin)
- * @author srowen@google.com (Sean Owen)
+ * @author Sean Owen
  * @author alasdair@google.com (Alasdair Mackintosh)
  */
 public abstract class AbstractUPCEANReader extends AbstractOneDReader implements UPCEANReader {
 
+  // These two values are critical for determining how permissive the decoding will be.
+  // We've arrived at these values through a lot of trial and error. Setting them any higher
+  // lets false positives creep in quickly.
   private static final int MAX_AVG_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.42f);
   private static final int MAX_INDIVIDUAL_VARIANCE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f);
 
   /**
    * Start/end guard pattern.
    */
-  private static final int[] START_END_PATTERN = {1, 1, 1,};
+  static final int[] START_END_PATTERN = {1, 1, 1,};
 
   /**
    * Pattern marking the middle of a UPC/EAN pattern, separating the two halves.
@@ -86,7 +90,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
 
   private final StringBuffer decodeRowStringBuffer;
 
-  public AbstractUPCEANReader() {
+  protected AbstractUPCEANReader() {
     decodeRowStringBuffer = new StringBuffer(20);
   }
 
@@ -98,9 +102,9 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
       startRange = findGuardPattern(row, nextStart, false, START_END_PATTERN);
       int start = startRange[0];
       nextStart = startRange[1];
-      // Make sure there is a quiet zone at least as big as the start pattern before the barcode. If
-      // this check would run off the left edge of the image, do not accept this barcode, as it is
-      // very likely to be a false positive.
+      // Make sure there is a quiet zone at least as big as the start pattern before the barcode.
+      // If this check would run off the left edge of the image, do not accept this barcode,
+      // as it is very likely to be a false positive.
       int quietStart = start - (nextStart - start);
       if (quietStart >= 0) {
         foundStart = row.isRange(quietStart, start, false);
@@ -109,27 +113,53 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     return startRange;
   }
 
-  public final Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException {
-    return decodeRow(rowNumber, row, findStartGuardPattern(row));
+  public final Result decodeRow(int rowNumber, BitArray row, Hashtable hints)
+      throws ReaderException {
+    return decodeRow(rowNumber, row, findStartGuardPattern(row), hints);
   }
 
-  public final Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) throws ReaderException {
+  public final Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, Hashtable hints)
+      throws ReaderException {
+
+    ResultPointCallback resultPointCallback = hints == null ? null :
+        (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+
+    if (resultPointCallback != null) {
+      resultPointCallback.foundPossibleResultPoint(new ResultPoint(
+          (startGuardRange[0] + startGuardRange[1]) / 2.0f, rowNumber
+      ));
+    }
+
     StringBuffer result = decodeRowStringBuffer;
     result.setLength(0);
     int endStart = decodeMiddle(row, startGuardRange, result);
+
+    if (resultPointCallback != null) {
+      resultPointCallback.foundPossibleResultPoint(new ResultPoint(
+          endStart, rowNumber
+      ));
+    }
+
     int[] endRange = decodeEnd(row, endStart);
 
+    if (resultPointCallback != null) {
+      resultPointCallback.foundPossibleResultPoint(new ResultPoint(
+          (endRange[0] + endRange[1]) / 2.0f, rowNumber
+      ));
+    }
+
+
     // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The
     // spec might want more whitespace, but in practice this is the maximum we can count on.
     int end = endRange[1];
     int quietEnd = end + (end - endRange[0]);
     if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false)) {
-      throw new ReaderException("Pattern not followed by whitespace");
+      throw ReaderException.getInstance();
     }
 
     String resultString = result.toString();
     if (!checkChecksum(resultString)) {
-      throw new ReaderException("Checksum failed");
+      throw ReaderException.getInstance();
     }
 
     float left = (float) (startGuardRange[1] + startGuardRange[0]) / 2.0f;
@@ -137,13 +167,20 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     return new Result(resultString,
         null, // no natural byte representation for these barcodes
         new ResultPoint[]{
-            new GenericResultPoint(left, (float) rowNumber),
-            new GenericResultPoint(right, (float) rowNumber)},
+            new ResultPoint(left, (float) rowNumber),
+            new ResultPoint(right, (float) rowNumber)},
         getBarcodeFormat());
   }
 
   abstract BarcodeFormat getBarcodeFormat();
 
+  /**
+   * @return {@link #checkStandardUPCEANChecksum(String)}
+   */
+  boolean checkChecksum(String s) throws ReaderException {
+    return checkStandardUPCEANChecksum(s);
+  }
+
   /**
    * Computes the UPC/EAN checksum on a string of digits, and reports
    * whether the checksum is correct or not.
@@ -152,7 +189,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
    * @return true iff string of digits passes the UPC/EAN checksum algorithm
    * @throws ReaderException if the string does not contain only digits
    */
-  boolean checkChecksum(String s) throws ReaderException {
+  private static boolean checkStandardUPCEANChecksum(String s) throws ReaderException {
     int length = s.length();
     if (length == 0) {
       return false;
@@ -162,7 +199,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     for (int i = length - 2; i >= 0; i -= 2) {
       int digit = (int) s.charAt(i) - (int) '0';
       if (digit < 0 || digit > 9) {
-        throw new ReaderException("Illegal character during checksum");
+        throw ReaderException.getInstance();
       }
       sum += digit;
     }
@@ -170,7 +207,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     for (int i = length - 1; i >= 0; i -= 2) {
       int digit = (int) s.charAt(i) - (int) '0';
       if (digit < 0 || digit > 9) {
-        throw new ReaderException("Illegal character during checksum");
+        throw ReaderException.getInstance();
       }
       sum += digit;
     }
@@ -178,7 +215,8 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
   }
 
   /**
-   * Subclasses override this to decode the portion of a barcode between the start and end guard patterns.
+   * Subclasses override this to decode the portion of a barcode between the start
+   * and end guard patterns.
    *
    * @param row row of black/white values to search
    * @param startRange start/end offset of start guard pattern
@@ -221,7 +259,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     int patternStart = rowOffset;
     for (int x = rowOffset; x < width; x++) {
       boolean pixel = row.get(x);
-      if ((!pixel && isWhite) || (pixel && !isWhite)) {
+      if (pixel ^ isWhite) {
         counters[counterPosition]++;
       } else {
         if (counterPosition == patternLength - 1) {
@@ -242,7 +280,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
         isWhite = !isWhite;
       }
     }
-    throw new ReaderException("Can't find pattern");
+    throw ReaderException.getInstance();
   }
 
   /**
@@ -274,7 +312,7 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
     if (bestMatch >= 0) {
       return bestMatch;
     } else {
-      throw new ReaderException("Could not match any digit in pattern");
+      throw ReaderException.getInstance();
     }
   }