Small style stuff
[zxing.git] / core / src / com / google / zxing / qrcode / detector / AlignmentPatternFinder.java
index 1e74bd9..c85cd72 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
- * Copyright 2007 Google Inc.\r
+ * Copyright 2007 ZXing authors\r
  *\r
  * Licensed under the Apache License, Version 2.0 (the "License");\r
  * you may not use this file except in compliance with the License.\r
 \r
 package com.google.zxing.qrcode.detector;\r
 \r
-import com.google.zxing.MonochromeBitmapSource;\r
-import com.google.zxing.ReaderException;\r
-import com.google.zxing.common.BitArray;\r
+import com.google.zxing.NotFoundException;\r
+import com.google.zxing.ResultPoint;\r
+import com.google.zxing.ResultPointCallback;\r
+import com.google.zxing.common.BitMatrix;\r
 \r
 import java.util.Vector;\r
 \r
 /**\r
+ * <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder\r
+ * patterns but are smaller and appear at regular intervals throughout the image.</p>\r
+ *\r
  * <p>At the moment this only looks for the bottom-right alignment pattern.</p>\r
  *\r
- * <p>This class is not thread-safe.</p>\r
+ * <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,\r
+ * pasted and stripped down here for maximum performance but does unfortunately duplicate\r
+ * some code.</p>\r
+ *\r
+ * <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.\r
  *\r
- * @author srowen@google.com (Sean Owen)\r
+ * @author Sean Owen\r
  */\r
 final class AlignmentPatternFinder {\r
 \r
-  private final MonochromeBitmapSource image;\r
+  private final BitMatrix image;\r
   private final Vector possibleCenters;\r
   private final int startX;\r
   private final int startY;\r
   private final int width;\r
   private final int height;\r
   private final float moduleSize;\r
+  private final int[] crossCheckStateCount;\r
+  private final ResultPointCallback resultPointCallback;\r
 \r
-  AlignmentPatternFinder(MonochromeBitmapSource image,\r
+  /**\r
+   * <p>Creates a finder that will look in a portion of the whole image.</p>\r
+   *\r
+   * @param image image to search\r
+   * @param startX left column from which to start searching\r
+   * @param startY top row from which to start searching\r
+   * @param width width of region to search\r
+   * @param height height of region to search\r
+   * @param moduleSize estimated module size so far\r
+   */\r
+  AlignmentPatternFinder(BitMatrix image,\r
                          int startX,\r
                          int startY,\r
                          int width,\r
                          int height,\r
-                         float moduleSize) {\r
+                         float moduleSize,\r
+                         ResultPointCallback resultPointCallback) {\r
     this.image = image;\r
     this.possibleCenters = new Vector(5);\r
     this.startX = startX;\r
@@ -52,41 +73,48 @@ final class AlignmentPatternFinder {
     this.width = width;\r
     this.height = height;\r
     this.moduleSize = moduleSize;\r
+    this.crossCheckStateCount = new int[3];\r
+    this.resultPointCallback = resultPointCallback;\r
   }\r
 \r
-  AlignmentPattern find() throws ReaderException {\r
+  /**\r
+   * <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since\r
+   * it's pretty performance-critical and so is written to be fast foremost.</p>\r
+   *\r
+   * @return {@link AlignmentPattern} if found\r
+   * @throws NotFoundException if not found\r
+   */\r
+  AlignmentPattern find() throws NotFoundException {\r
     int startX = this.startX;\r
     int height = this.height;\r
     int maxJ = startX + width;\r
     int middleI = startY + (height >> 1);\r
-    BitArray luminanceRow = new BitArray(width);\r
-    int[] stateCount = new int[3]; // looking for 1 1 1\r
+    // We are looking for black/white/black modules in 1:1:1 ratio;\r
+    // this tracks the number of black/white/black modules seen so far\r
+    int[] stateCount = new int[3];\r
     for (int iGen = 0; iGen < height; iGen++) {\r
       // Search from middle outwards\r
-      int i = middleI +\r
-          ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));\r
-      image.getBlackRow(i, luminanceRow, startX, width);\r
+      int i = middleI + ((iGen & 0x01) == 0 ? ((iGen + 1) >> 1) : -((iGen + 1) >> 1));\r
       stateCount[0] = 0;\r
       stateCount[1] = 0;\r
       stateCount[2] = 0;\r
-      int currentState = 0;\r
       int j = startX;\r
       // Burn off leading white pixels before anything else; if we start in the middle of\r
       // a white run, it doesn't make sense to count its length, since we don't know if the\r
       // white run continued to the left of the start point\r
-      while (!luminanceRow.get(j - startX) && j < maxJ) {\r
+      while (j < maxJ && !image.get(j, i)) {\r
         j++;\r
       }\r
+      int currentState = 0;\r
       while (j < maxJ) {\r
-        if (luminanceRow.get(j - startX)) {\r
+        if (image.get(j, i)) {\r
           // Black pixel\r
           if (currentState == 1) { // Counting black pixels\r
             stateCount[currentState]++;\r
           } else { // Counting white pixels\r
             if (currentState == 2) { // A winner?\r
               if (foundPatternCross(stateCount)) { // Yes\r
-                AlignmentPattern confirmed =\r
-                    handlePossibleCenter(stateCount, i, j);\r
+                AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j);\r
                 if (confirmed != null) {\r
                   return confirmed;\r
                 }\r
@@ -122,37 +150,65 @@ final class AlignmentPatternFinder {
       return (AlignmentPattern) possibleCenters.elementAt(0);\r
     }\r
 \r
-    throw new ReaderException("Could not find alignment pattern");\r
+    throw NotFoundException.getNotFoundInstance();\r
   }\r
 \r
+  /**\r
+   * Given a count of black/white/black pixels just seen and an end position,\r
+   * figures the location of the center of this black/white/black run.\r
+   */\r
   private static float centerFromEnd(int[] stateCount, int end) {\r
     return (float) (end - stateCount[2]) - stateCount[1] / 2.0f;\r
   }\r
 \r
+  /**\r
+   * @param stateCount count of black/white/black pixels just read\r
+   * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios\r
+   *         used by alignment patterns to be considered a match\r
+   */\r
   private boolean foundPatternCross(int[] stateCount) {\r
     float moduleSize = this.moduleSize;\r
+    float maxVariance = moduleSize / 2.0f;\r
     for (int i = 0; i < 3; i++) {\r
-      if (2.0f * Math.abs(moduleSize - stateCount[i]) >= moduleSize) {\r
+      if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) {\r
         return false;\r
       }\r
     }\r
     return true;\r
   }\r
 \r
-  private float crossCheckVertical(int startI, int centerJ, int maxCount) {\r
-    MonochromeBitmapSource image = this.image;\r
+  /**\r
+   * <p>After a horizontal scan finds a potential alignment pattern, this method\r
+   * "cross-checks" by scanning down vertically through the center of the possible\r
+   * alignment pattern to see if the same proportion is detected.</p>\r
+   *\r
+   * @param startI row where an alignment pattern was detected\r
+   * @param centerJ center of the section that appears to cross an alignment pattern\r
+   * @param maxCount maximum reasonable number of modules that should be\r
+   * observed in any reading state, based on the results of the horizontal scan\r
+   * @return vertical center of alignment pattern, or {@link Float#NaN} if not found\r
+   */\r
+  private float crossCheckVertical(int startI, int centerJ, int maxCount,\r
+      int originalStateCountTotal) {\r
+    BitMatrix image = this.image;\r
 \r
     int maxI = image.getHeight();\r
-    int[] stateCount = new int[3];\r
+    int[] stateCount = crossCheckStateCount;\r
+    stateCount[0] = 0;\r
+    stateCount[1] = 0;\r
+    stateCount[2] = 0;\r
+\r
+    // Start counting up from center\r
     int i = startI;\r
-    while (i >= 0 && image.isBlack(centerJ, i) && stateCount[1] <= maxCount) {\r
+    while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) {\r
       stateCount[1]++;\r
       i--;\r
     }\r
+    // If already too many modules in this state or ran off the edge:\r
     if (i < 0 || stateCount[1] > maxCount) {\r
       return Float.NaN;\r
     }\r
-    while (i >= 0 && !image.isBlack(centerJ, i) && stateCount[0] <= maxCount) {\r
+    while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) {\r
       stateCount[0]++;\r
       i--;\r
     }\r
@@ -160,17 +216,16 @@ final class AlignmentPatternFinder {
       return Float.NaN;\r
     }\r
 \r
+    // Now also count down from center\r
     i = startI + 1;\r
-    while (i < maxI && image.isBlack(centerJ, i) &&\r
-        stateCount[1] <= maxCount) {\r
+    while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) {\r
       stateCount[1]++;\r
       i++;\r
     }\r
     if (i == maxI || stateCount[1] > maxCount) {\r
       return Float.NaN;\r
     }\r
-    while (i < maxI && !image.isBlack(centerJ, i) &&\r
-        stateCount[2] <= maxCount) {\r
+    while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) {\r
       stateCount[2]++;\r
       i++;\r
     }\r
@@ -178,21 +233,31 @@ final class AlignmentPatternFinder {
       return Float.NaN;\r
     }\r
 \r
-    return\r
-        foundPatternCross(stateCount) ?\r
-            centerFromEnd(stateCount, i) :\r
-            Float.NaN;\r
+    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\r
+    if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) {\r
+      return Float.NaN;\r
+    }\r
+\r
+    return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN;\r
   }\r
 \r
-  private AlignmentPattern handlePossibleCenter(int[] stateCount,\r
-                                                int i,\r
-                                                int j) {\r
+  /**\r
+   * <p>This is called when a horizontal scan finds a possible alignment pattern. It will\r
+   * cross check with a vertical scan, and if successful, will see if this pattern had been\r
+   * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have\r
+   * found the alignment pattern.</p>\r
+   *\r
+   * @param stateCount reading state module counts from horizontal scan\r
+   * @param i row where alignment pattern may be found\r
+   * @param j end of possible alignment pattern in row\r
+   * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not\r
+   */\r
+  private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) {\r
+    int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2];\r
     float centerJ = centerFromEnd(stateCount, j);\r
-    float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1]);\r
+    float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal);\r
     if (!Float.isNaN(centerI)) {\r
-      float estimatedModuleSize = (float) (stateCount[0] +\r
-          stateCount[1] +\r
-          stateCount[2]) / 3.0f;\r
+      float estimatedModuleSize = (float) (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f;\r
       int max = possibleCenters.size();\r
       for (int index = 0; index < max; index++) {\r
         AlignmentPattern center = (AlignmentPattern) possibleCenters.elementAt(index);\r
@@ -202,9 +267,13 @@ final class AlignmentPatternFinder {
         }\r
       }\r
       // Hadn't found this before; save it\r
-      possibleCenters.addElement(new AlignmentPattern(centerJ, centerI, estimatedModuleSize));\r
+      ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);\r
+      possibleCenters.addElement(point);\r
+      if (resultPointCallback != null) {\r
+        resultPointCallback.foundPossibleResultPoint(point);\r
+      }\r
     }\r
     return null;\r
   }\r
 \r
-}
\ No newline at end of file
+}\r