Draft of 'thinking' visualization for barcode scanning. Works for 1D and QR codes.
authorsrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Mon, 23 Nov 2009 16:42:31 +0000 (16:42 +0000)
committersrowen <srowen@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Mon, 23 Nov 2009 16:42:31 +0000 (16:42 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@1125 59b500cc-1b3d-0410-9834-0bbf25fbcc57

17 files changed:
android/res/values/colors.xml
android/src/com/google/zxing/client/android/CaptureActivity.java
android/src/com/google/zxing/client/android/CaptureActivityHandler.java
android/src/com/google/zxing/client/android/DecodeThread.java
android/src/com/google/zxing/client/android/ViewfinderResultPointCallback.java [new file with mode: 0644]
android/src/com/google/zxing/client/android/ViewfinderView.java
core/src/com/google/zxing/DecodeHintType.java
core/src/com/google/zxing/ResultPointCallback.java [new file with mode: 0644]
core/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
core/src/com/google/zxing/oned/AbstractOneDReader.java
core/src/com/google/zxing/oned/AbstractUPCEANReader.java
core/src/com/google/zxing/oned/MultiFormatUPCEANReader.java
core/src/com/google/zxing/oned/UPCAReader.java
core/src/com/google/zxing/oned/UPCEANReader.java
core/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
core/src/com/google/zxing/qrcode/detector/Detector.java
core/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java

index 9584a67..1872d5b 100755 (executable)
@@ -19,6 +19,7 @@
   <color name="encode_view">#ffffffff</color>
   <color name="help_button_view">#ffcccccc</color>
   <color name="help_view">#ff404040</color>
+  <color name="possible_result_points">#c0ffff00</color>  
   <color name="result_image_border">#ffffffff</color>
   <color name="result_minor_text">#ffc0c0c0</color>
   <color name="result_points">#c000ff00</color>
index 7ff6ad0..0ebc30d 100755 (executable)
@@ -125,6 +125,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
     }
   };
 
+  ViewfinderView getViewfinderView() {
+    return viewfinderView;
+  }
+
   public Handler getHandler() {
     return handler;
   }
index ab241c7..fd67160 100755 (executable)
@@ -42,10 +42,12 @@ public final class CaptureActivityHandler extends Handler {
     DONE
   }
 
-  CaptureActivityHandler(CaptureActivity activity, String decodeMode,
-                                 boolean beginScanning) {
+  CaptureActivityHandler(CaptureActivity activity,
+                         String decodeMode,
+                         boolean beginScanning) {
     this.activity = activity;
-    decodeThread = new DecodeThread(activity, decodeMode);
+    decodeThread = new DecodeThread(activity, decodeMode,
+        new ViewfinderResultPointCallback(activity.getViewfinderView()));
     decodeThread.start();
     state = State.SUCCESS;
 
index 1c36e84..ca2afea 100755 (executable)
@@ -22,6 +22,7 @@ import com.google.zxing.DecodeHintType;
 import com.google.zxing.MultiFormatReader;
 import com.google.zxing.ReaderException;
 import com.google.zxing.Result;
+import com.google.zxing.ResultPointCallback;
 import com.google.zxing.common.GlobalHistogramBinarizer;
 
 import android.content.SharedPreferences;
@@ -47,10 +48,12 @@ final class DecodeThread extends Thread {
   private Handler handler;
   private final CaptureActivity activity;
   private final MultiFormatReader multiFormatReader;
+  private final ResultPointCallback resultPointCallback;
 
-  DecodeThread(CaptureActivity activity, String mode) {
+  DecodeThread(CaptureActivity activity, String mode, ResultPointCallback resultPointCallback) {
     this.activity = activity;
     multiFormatReader = new MultiFormatReader();
+    this.resultPointCallback = resultPointCallback;
 
     // The prefs can't change while the thread is running, so pick them up once here.
     if (mode == null || mode.length() == 0) {
@@ -101,39 +104,27 @@ final class DecodeThread extends Thread {
   }
 
   private void setDecodeProductMode() {
-    Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
-    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(4);
-    vector.addElement(BarcodeFormat.UPC_A);
-    vector.addElement(BarcodeFormat.UPC_E);
-    vector.addElement(BarcodeFormat.EAN_13);
-    vector.addElement(BarcodeFormat.EAN_8);
-    hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
-    multiFormatReader.setHints(hints);
+    doSetDecodeMode(BarcodeFormat.UPC_A,
+                    BarcodeFormat.UPC_E,
+                    BarcodeFormat.EAN_13,
+                    BarcodeFormat.EAN_8);
   }
 
   /**
    * Select the 1D formats we want this client to decode by hand.
    */
   private void setDecode1DMode() {
-    Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
-    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(7);
-    vector.addElement(BarcodeFormat.UPC_A);
-    vector.addElement(BarcodeFormat.UPC_E);
-    vector.addElement(BarcodeFormat.EAN_13);
-    vector.addElement(BarcodeFormat.EAN_8);
-    vector.addElement(BarcodeFormat.CODE_39);
-    vector.addElement(BarcodeFormat.CODE_128);
-    vector.addElement(BarcodeFormat.ITF);
-    hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
-    multiFormatReader.setHints(hints);
+    doSetDecodeMode(BarcodeFormat.UPC_A,
+                    BarcodeFormat.UPC_E,
+                    BarcodeFormat.EAN_13,
+                    BarcodeFormat.EAN_8,
+                    BarcodeFormat.CODE_39,
+                    BarcodeFormat.CODE_128,
+                    BarcodeFormat.ITF);
   }
 
   private void setDecodeQRMode() {
-    Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
-    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(1);
-    vector.addElement(BarcodeFormat.QR_CODE);
-    hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
-    multiFormatReader.setHints(hints);
+    doSetDecodeMode(BarcodeFormat.QR_CODE);
   }
 
   /**
@@ -141,17 +132,24 @@ final class DecodeThread extends Thread {
    * explicitly set which formats are available.
    */
   private void setDecodeAllMode() {
+    doSetDecodeMode(BarcodeFormat.UPC_A,
+                    BarcodeFormat.UPC_E,
+                    BarcodeFormat.EAN_13,
+                    BarcodeFormat.EAN_8,
+                    BarcodeFormat.CODE_39,
+                    BarcodeFormat.CODE_128,
+                    BarcodeFormat.ITF,
+                    BarcodeFormat.QR_CODE);
+  }
+
+  private void doSetDecodeMode(BarcodeFormat... formats) {
     Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
-    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(8);
-    vector.addElement(BarcodeFormat.UPC_A);
-    vector.addElement(BarcodeFormat.UPC_E);
-    vector.addElement(BarcodeFormat.EAN_13);
-    vector.addElement(BarcodeFormat.EAN_8);
-    vector.addElement(BarcodeFormat.CODE_39);
-    vector.addElement(BarcodeFormat.CODE_128);
-    vector.addElement(BarcodeFormat.ITF);
-    vector.addElement(BarcodeFormat.QR_CODE);
+    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(formats.length);
+    for (BarcodeFormat format : formats) {
+      vector.addElement(format);
+    }
     hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
+    hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
     multiFormatReader.setHints(hints);
   }
 
diff --git a/android/src/com/google/zxing/client/android/ViewfinderResultPointCallback.java b/android/src/com/google/zxing/client/android/ViewfinderResultPointCallback.java
new file mode 100644 (file)
index 0000000..3754769
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+final class ViewfinderResultPointCallback implements ResultPointCallback {
+
+  private final ViewfinderView viewfinderView;
+
+  ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
+    this.viewfinderView = viewfinderView;
+  }
+
+  public void foundPossibleResultPoint(ResultPoint point) {
+    viewfinderView.addPossibleResultPoint(point);
+  }
+
+}
index 3b73753..b972077 100755 (executable)
@@ -24,6 +24,10 @@ import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import com.google.zxing.ResultPoint;
+
+import java.util.Collection;
+import java.util.HashSet;
 
 /**
  * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
@@ -34,15 +38,18 @@ import android.view.View;
 public final class ViewfinderView extends View {
   private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
   private static final long ANIMATION_DELAY = 100L;
+  private static final int OPAQUE = 0xFF;
 
   private final Paint paint;
-  private final Rect box;
   private Bitmap resultBitmap;
   private final int maskColor;
   private final int resultColor;
   private final int frameColor;
   private final int laserColor;
+  private final int resultPointColor;
   private int scannerAlpha;
+  private Collection<ResultPoint> possibleResultPoints;
+  private Collection<ResultPoint> lastPossibleResultPoints;
 
   // This constructor is used when the class is built from an XML resource.
   public ViewfinderView(Context context, AttributeSet attrs) {
@@ -50,13 +57,14 @@ public final class ViewfinderView extends View {
 
     // Initialize these once for performance rather than calling them every time in onDraw().
     paint = new Paint();
-    box = new Rect();
     Resources resources = getResources();
     maskColor = resources.getColor(R.color.viewfinder_mask);
     resultColor = resources.getColor(R.color.result_view);
     frameColor = resources.getColor(R.color.viewfinder_frame);
     laserColor = resources.getColor(R.color.viewfinder_laser);
+    resultPointColor = resources.getColor(R.color.possible_result_points);
     scannerAlpha = 0;
+    possibleResultPoints = new HashSet<ResultPoint>(5);
   }
 
   @Override
@@ -70,42 +78,55 @@ public final class ViewfinderView extends View {
 
     // Draw the exterior (i.e. outside the framing rect) darkened
     paint.setColor(resultBitmap != null ? resultColor : maskColor);
-    box.set(0, 0, width, frame.top);
-    canvas.drawRect(box, paint);
-    box.set(0, frame.top, frame.left, frame.bottom + 1);
-    canvas.drawRect(box, paint);
-    box.set(frame.right + 1, frame.top, width, frame.bottom + 1);
-    canvas.drawRect(box, paint);
-    box.set(0, frame.bottom + 1, width, height);
-    canvas.drawRect(box, paint);
+    canvas.drawRect(0, 0, width, frame.top, paint);
+    canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
+    canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
+    canvas.drawRect(0, frame.bottom + 1, width, height, paint);
 
     if (resultBitmap != null) {
       // Draw the opaque result bitmap over the scanning rectangle
-      paint.setAlpha(255);
+      paint.setAlpha(OPAQUE);
       canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
     } else {
+
       // Draw a two pixel solid black border inside the framing rect
       paint.setColor(frameColor);
-      box.set(frame.left, frame.top, frame.right + 1, frame.top + 2);
-      canvas.drawRect(box, paint);
-      box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1);
-      canvas.drawRect(box, paint);
-      box.set(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1);
-      canvas.drawRect(box, paint);
-      box.set(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1);
-      canvas.drawRect(box, paint);
+      canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
+      canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
+      canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
+      canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
 
       // Draw a red "laser scanner" line through the middle to show decoding is active
       paint.setColor(laserColor);
       paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
       scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
       int middle = frame.height() / 2 + frame.top;
-      box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2);
-      canvas.drawRect(box, paint);
+      canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
+
+      Collection<ResultPoint> currentPossible = possibleResultPoints;
+      Collection<ResultPoint> currentLast = lastPossibleResultPoints;
+      if (currentPossible.isEmpty()) {
+        lastPossibleResultPoints = null;
+      } else {
+        possibleResultPoints = new HashSet<ResultPoint>(5);
+        lastPossibleResultPoints = currentPossible;
+        paint.setAlpha(OPAQUE);
+        paint.setColor(resultPointColor);
+        for (ResultPoint point : currentPossible) {
+          canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint);
+        }
+      }
+      if (currentLast != null) {
+        paint.setAlpha(OPAQUE / 2);
+        paint.setColor(resultPointColor);
+        for (ResultPoint point : currentLast) {
+          canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint);
+        }
+      }
 
       // Request another update at the animation interval, but only repaint the laser line,
       // not the entire viewfinder mask.
-      postInvalidateDelayed(ANIMATION_DELAY, box.left, box.top, box.right, box.bottom);
+      postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
     }
   }
 
@@ -123,4 +144,9 @@ public final class ViewfinderView extends View {
     resultBitmap = barcode;
     invalidate();
   }
+
+  public void addPossibleResultPoint(ResultPoint point) {
+    possibleResultPoints.add(point);
+  }
+
 }
index 21a4de0..2c863dd 100644 (file)
@@ -62,6 +62,12 @@ public final class DecodeHintType {
    */
   public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
 
+  /**
+   * The caller needs to be notified via callback when a possible {@link ResultPoint}
+   * is found. Maps to a {@link ResultPointCallback}.
+   */
+  public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
+
   private DecodeHintType() {
   }
 
diff --git a/core/src/com/google/zxing/ResultPointCallback.java b/core/src/com/google/zxing/ResultPointCallback.java
new file mode 100644 (file)
index 0000000..0c85410
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2009 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing;
+
+/**
+ * Callback which is invoked when a possible result point (significant
+ * point in the barcode image such as a corner) is found.
+ *
+ * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
+ */
+public interface ResultPointCallback {
+
+  void foundPossibleResultPoint(ResultPoint point);
+
+}
index 96f04d1..d133400 100644 (file)
@@ -19,6 +19,7 @@ package com.google.zxing.multi.qrcode.detector;
 import com.google.zxing.DecodeHintType;\r
 import com.google.zxing.ReaderException;\r
 import com.google.zxing.ResultPoint;\r
+import com.google.zxing.ResultPointCallback;\r
 import com.google.zxing.common.Collections;\r
 import com.google.zxing.common.Comparator;\r
 import com.google.zxing.common.BitMatrix;\r
@@ -90,6 +91,10 @@ final class MultiFinderPatternFinder extends FinderPatternFinder {
     super(image);\r
   }\r
 \r
+  MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {\r
+    super(image, resultPointCallback);\r
+  }\r
+\r
   /**\r
    * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are\r
    *         those that have been detected at least {@link #CENTER_QUORUM} times, and whose module\r
index 10e0b60..93e3661 100644 (file)
@@ -118,6 +118,14 @@ public abstract class AbstractOneDReader implements OneDReader {
       for (int attempt = 0; attempt < 2; attempt++) {
         if (attempt == 1) { // trying again?
           row.reverse(); // reverse the row and continue
+          // This means we will only ever draw result points *once* in the life of this method
+          // since we want to avoid drawing the wrong points after flipping the row, and,
+          // don't want to clutter with noise from every single row scan -- just the scans
+          // that start on the center line.
+          if (hints != null && hints.containsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)) {
+            hints = (Hashtable) hints.clone();
+            hints.remove(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+          }
         }
         try {
           // Look for a barcode
index 0d1b26f..f25cdf4 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 java.util.Hashtable;
@@ -110,16 +112,40 @@ public abstract class AbstractUPCEANReader extends AbstractOneDReader implements
 
   public final Result decodeRow(int rowNumber, BitArray row, Hashtable hints)
       throws ReaderException {
-    return decodeRow(rowNumber, row, findStartGuardPattern(row));
+    return decodeRow(rowNumber, row, findStartGuardPattern(row), hints);
   }
 
-  public final Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange)
+  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];
index eb03890..295df32 100644 (file)
@@ -68,7 +68,7 @@ public final class MultiFormatUPCEANReader extends AbstractOneDReader {
       UPCEANReader reader = (UPCEANReader) readers.elementAt(i);
       Result result;
       try {
-        result = reader.decodeRow(rowNumber, row, startGuardPattern);
+        result = reader.decodeRow(rowNumber, row, startGuardPattern, hints);
       } catch (ReaderException re) {
         continue;
       }
index be4bf85..5299b03 100644 (file)
@@ -34,8 +34,9 @@ public final class UPCAReader implements UPCEANReader {
 
   private final UPCEANReader ean13Reader = new EAN13Reader();
 
-  public Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) throws ReaderException {
-    return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange));
+  public Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, Hashtable hints)
+      throws ReaderException {
+    return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange, hints));
   }
 
   public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException {
index 054bdf9..db1dcb1 100644 (file)
@@ -20,6 +20,8 @@ import com.google.zxing.ReaderException;
 import com.google.zxing.Result;
 import com.google.zxing.common.BitArray;
 
+import java.util.Hashtable;
+
 /**
  * <p>This interfaces captures additional functionality that readers of
  * UPC/EAN family of barcodes should expose.</p>
@@ -33,6 +35,7 @@ public interface UPCEANReader extends OneDReader {
    * allows caller to inform method about where the UPC/EAN start pattern is
    * found. This allows this to be computed once and reused across many implementations.</p>
    */
-  Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) throws ReaderException;
+  Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, Hashtable hints)
+      throws ReaderException;
 
 }
\ No newline at end of file
index c1d5c5b..df158b6 100644 (file)
@@ -17,6 +17,8 @@
 package com.google.zxing.qrcode.detector;\r
 \r
 import com.google.zxing.ReaderException;\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
@@ -45,6 +47,7 @@ final class AlignmentPatternFinder {
   private final int height;\r
   private final float moduleSize;\r
   private final int[] crossCheckStateCount;\r
+  private final ResultPointCallback resultPointCallback;\r
 \r
   /**\r
    * <p>Creates a finder that will look in a portion of the whole image.</p>\r
@@ -61,7 +64,8 @@ final class AlignmentPatternFinder {
                          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
@@ -70,6 +74,7 @@ final class AlignmentPatternFinder {
     this.height = height;\r
     this.moduleSize = moduleSize;\r
     this.crossCheckStateCount = new int[3];\r
+    this.resultPointCallback = resultPointCallback;\r
   }\r
 \r
   /**\r
@@ -262,7 +267,11 @@ 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
index 9e3cd3d..5b8b248 100644 (file)
 
 package com.google.zxing.qrcode.detector;
 
+import com.google.zxing.DecodeHintType;
 import com.google.zxing.ReaderException;
 import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
 import com.google.zxing.common.BitMatrix;
 import com.google.zxing.common.DetectorResult;
 import com.google.zxing.common.GridSampler;
@@ -34,6 +36,7 @@ import java.util.Hashtable;
 public class Detector {
 
   private final BitMatrix image;
+  private ResultPointCallback resultPointCallback;
 
   public Detector(BitMatrix image) {
     this.image = image;
@@ -62,7 +65,10 @@ public class Detector {
    */
   public DetectorResult detect(Hashtable hints) throws ReaderException {
 
-    FinderPatternFinder finder = new FinderPatternFinder(image);
+    resultPointCallback = hints == null ? null :
+        (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
+
+    FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback);
     FinderPatternInfo info = finder.find(hints);
 
     return processFinderPatternInfo(info);
@@ -347,7 +353,8 @@ public class Detector {
             alignmentAreaTopY,
             alignmentAreaRightX - alignmentAreaLeftX,
             alignmentAreaBottomY - alignmentAreaTopY,
-            overallEstModuleSize);
+            overallEstModuleSize,
+            resultPointCallback);
     return alignmentFinder.find();
   }
 
index 1d819ec..eb23e79 100755 (executable)
@@ -19,6 +19,7 @@ package com.google.zxing.qrcode.detector;
 import com.google.zxing.DecodeHintType;\r
 import com.google.zxing.ReaderException;\r
 import com.google.zxing.ResultPoint;\r
+import com.google.zxing.ResultPointCallback;\r
 import com.google.zxing.common.Collections;\r
 import com.google.zxing.common.Comparator;\r
 import com.google.zxing.common.BitMatrix;\r
@@ -45,6 +46,7 @@ public class FinderPatternFinder {
   private final Vector possibleCenters;\r
   private boolean hasSkipped;\r
   private final int[] crossCheckStateCount;\r
+  private final ResultPointCallback resultPointCallback;\r
 \r
   /**\r
    * <p>Creates a finder that will search the image for three finder patterns.</p>\r
@@ -52,9 +54,14 @@ public class FinderPatternFinder {
    * @param image image to search\r
    */\r
   public FinderPatternFinder(BitMatrix image) {\r
+    this(image, null);\r
+  }\r
+\r
+  public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {\r
     this.image = image;\r
     this.possibleCenters = new Vector();\r
     this.crossCheckStateCount = new int[5];\r
+    this.resultPointCallback = resultPointCallback;\r
   }\r
 \r
   protected BitMatrix getImage() {\r
@@ -401,7 +408,11 @@ public class FinderPatternFinder {
           }\r
         }\r
         if (!found) {\r
-          possibleCenters.addElement(new FinderPattern(centerJ, centerI, estimatedModuleSize));\r
+          ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);\r
+          possibleCenters.addElement(point);\r
+          if (resultPointCallback != null) {\r
+            resultPointCallback.foundPossibleResultPoint(point);\r
+          }\r
         }\r
         return true;\r
       }\r