Draft of 'thinking' visualization for barcode scanning. Works for 1D and QR codes.

git-svn-id: https://zxing.googlecode.com/svn/trunk@1125 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2009-11-23 16:42:31 +00:00
parent aee98771a2
commit d1973dce0a
17 changed files with 240 additions and 70 deletions

View file

@ -19,6 +19,7 @@
<color name="encode_view">#ffffffff</color> <color name="encode_view">#ffffffff</color>
<color name="help_button_view">#ffcccccc</color> <color name="help_button_view">#ffcccccc</color>
<color name="help_view">#ff404040</color> <color name="help_view">#ff404040</color>
<color name="possible_result_points">#c0ffff00</color>
<color name="result_image_border">#ffffffff</color> <color name="result_image_border">#ffffffff</color>
<color name="result_minor_text">#ffc0c0c0</color> <color name="result_minor_text">#ffc0c0c0</color>
<color name="result_points">#c000ff00</color> <color name="result_points">#c000ff00</color>

View file

@ -125,6 +125,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
} }
}; };
ViewfinderView getViewfinderView() {
return viewfinderView;
}
public Handler getHandler() { public Handler getHandler() {
return handler; return handler;
} }

View file

@ -42,10 +42,12 @@ public final class CaptureActivityHandler extends Handler {
DONE DONE
} }
CaptureActivityHandler(CaptureActivity activity, String decodeMode, CaptureActivityHandler(CaptureActivity activity,
boolean beginScanning) { String decodeMode,
boolean beginScanning) {
this.activity = activity; this.activity = activity;
decodeThread = new DecodeThread(activity, decodeMode); decodeThread = new DecodeThread(activity, decodeMode,
new ViewfinderResultPointCallback(activity.getViewfinderView()));
decodeThread.start(); decodeThread.start();
state = State.SUCCESS; state = State.SUCCESS;

View file

@ -22,6 +22,7 @@ import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatReader;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.GlobalHistogramBinarizer; import com.google.zxing.common.GlobalHistogramBinarizer;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -47,10 +48,12 @@ final class DecodeThread extends Thread {
private Handler handler; private Handler handler;
private final CaptureActivity activity; private final CaptureActivity activity;
private final MultiFormatReader multiFormatReader; private final MultiFormatReader multiFormatReader;
private final ResultPointCallback resultPointCallback;
DecodeThread(CaptureActivity activity, String mode) { DecodeThread(CaptureActivity activity, String mode, ResultPointCallback resultPointCallback) {
this.activity = activity; this.activity = activity;
multiFormatReader = new MultiFormatReader(); multiFormatReader = new MultiFormatReader();
this.resultPointCallback = resultPointCallback;
// The prefs can't change while the thread is running, so pick them up once here. // The prefs can't change while the thread is running, so pick them up once here.
if (mode == null || mode.length() == 0) { if (mode == null || mode.length() == 0) {
@ -101,39 +104,27 @@ final class DecodeThread extends Thread {
} }
private void setDecodeProductMode() { private void setDecodeProductMode() {
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3); doSetDecodeMode(BarcodeFormat.UPC_A,
Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(4); BarcodeFormat.UPC_E,
vector.addElement(BarcodeFormat.UPC_A); BarcodeFormat.EAN_13,
vector.addElement(BarcodeFormat.UPC_E); BarcodeFormat.EAN_8);
vector.addElement(BarcodeFormat.EAN_13);
vector.addElement(BarcodeFormat.EAN_8);
hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
multiFormatReader.setHints(hints);
} }
/** /**
* Select the 1D formats we want this client to decode by hand. * Select the 1D formats we want this client to decode by hand.
*/ */
private void setDecode1DMode() { private void setDecode1DMode() {
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3); doSetDecodeMode(BarcodeFormat.UPC_A,
Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(7); BarcodeFormat.UPC_E,
vector.addElement(BarcodeFormat.UPC_A); BarcodeFormat.EAN_13,
vector.addElement(BarcodeFormat.UPC_E); BarcodeFormat.EAN_8,
vector.addElement(BarcodeFormat.EAN_13); BarcodeFormat.CODE_39,
vector.addElement(BarcodeFormat.EAN_8); BarcodeFormat.CODE_128,
vector.addElement(BarcodeFormat.CODE_39); BarcodeFormat.ITF);
vector.addElement(BarcodeFormat.CODE_128);
vector.addElement(BarcodeFormat.ITF);
hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
multiFormatReader.setHints(hints);
} }
private void setDecodeQRMode() { private void setDecodeQRMode() {
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3); doSetDecodeMode(BarcodeFormat.QR_CODE);
Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(1);
vector.addElement(BarcodeFormat.QR_CODE);
hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
multiFormatReader.setHints(hints);
} }
/** /**
@ -141,17 +132,24 @@ final class DecodeThread extends Thread {
* explicitly set which formats are available. * explicitly set which formats are available.
*/ */
private void setDecodeAllMode() { 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); Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(8); Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>(formats.length);
vector.addElement(BarcodeFormat.UPC_A); for (BarcodeFormat format : formats) {
vector.addElement(BarcodeFormat.UPC_E); vector.addElement(format);
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);
hints.put(DecodeHintType.POSSIBLE_FORMATS, vector); hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
multiFormatReader.setHints(hints); multiFormatReader.setHints(hints);
} }

View file

@ -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);
}
}

View file

@ -24,6 +24,10 @@ import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; 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 * 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 { public final class ViewfinderView extends View {
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
private static final long ANIMATION_DELAY = 100L; private static final long ANIMATION_DELAY = 100L;
private static final int OPAQUE = 0xFF;
private final Paint paint; private final Paint paint;
private final Rect box;
private Bitmap resultBitmap; private Bitmap resultBitmap;
private final int maskColor; private final int maskColor;
private final int resultColor; private final int resultColor;
private final int frameColor; private final int frameColor;
private final int laserColor; private final int laserColor;
private final int resultPointColor;
private int scannerAlpha; private int scannerAlpha;
private Collection<ResultPoint> possibleResultPoints;
private Collection<ResultPoint> lastPossibleResultPoints;
// This constructor is used when the class is built from an XML resource. // This constructor is used when the class is built from an XML resource.
public ViewfinderView(Context context, AttributeSet attrs) { 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(). // Initialize these once for performance rather than calling them every time in onDraw().
paint = new Paint(); paint = new Paint();
box = new Rect();
Resources resources = getResources(); Resources resources = getResources();
maskColor = resources.getColor(R.color.viewfinder_mask); maskColor = resources.getColor(R.color.viewfinder_mask);
resultColor = resources.getColor(R.color.result_view); resultColor = resources.getColor(R.color.result_view);
frameColor = resources.getColor(R.color.viewfinder_frame); frameColor = resources.getColor(R.color.viewfinder_frame);
laserColor = resources.getColor(R.color.viewfinder_laser); laserColor = resources.getColor(R.color.viewfinder_laser);
resultPointColor = resources.getColor(R.color.possible_result_points);
scannerAlpha = 0; scannerAlpha = 0;
possibleResultPoints = new HashSet<ResultPoint>(5);
} }
@Override @Override
@ -70,42 +78,55 @@ public final class ViewfinderView extends View {
// Draw the exterior (i.e. outside the framing rect) darkened // Draw the exterior (i.e. outside the framing rect) darkened
paint.setColor(resultBitmap != null ? resultColor : maskColor); paint.setColor(resultBitmap != null ? resultColor : maskColor);
box.set(0, 0, width, frame.top); canvas.drawRect(0, 0, width, frame.top, paint);
canvas.drawRect(box, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
box.set(0, frame.top, frame.left, frame.bottom + 1); canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
canvas.drawRect(box, paint); canvas.drawRect(0, frame.bottom + 1, width, height, 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);
if (resultBitmap != null) { if (resultBitmap != null) {
// Draw the opaque result bitmap over the scanning rectangle // Draw the opaque result bitmap over the scanning rectangle
paint.setAlpha(255); paint.setAlpha(OPAQUE);
canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint); canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
} else { } else {
// Draw a two pixel solid black border inside the framing rect // Draw a two pixel solid black border inside the framing rect
paint.setColor(frameColor); paint.setColor(frameColor);
box.set(frame.left, frame.top, frame.right + 1, frame.top + 2); canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
canvas.drawRect(box, paint); canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
box.set(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1); canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
canvas.drawRect(box, paint); canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, 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);
// Draw a red "laser scanner" line through the middle to show decoding is active // Draw a red "laser scanner" line through the middle to show decoding is active
paint.setColor(laserColor); paint.setColor(laserColor);
paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
int middle = frame.height() / 2 + frame.top; int middle = frame.height() / 2 + frame.top;
box.set(frame.left + 2, middle - 1, frame.right - 1, middle + 2); canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint);
canvas.drawRect(box, 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, // Request another update at the animation interval, but only repaint the laser line,
// not the entire viewfinder mask. // 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; resultBitmap = barcode;
invalidate(); invalidate();
} }
public void addPossibleResultPoint(ResultPoint point) {
possibleResultPoints.add(point);
}
} }

View file

@ -62,6 +62,12 @@ public final class DecodeHintType {
*/ */
public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new 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() { private DecodeHintType() {
} }

View file

@ -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);
}

View file

@ -19,6 +19,7 @@ package com.google.zxing.multi.qrcode.detector;
import com.google.zxing.DecodeHintType; import com.google.zxing.DecodeHintType;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.Collections; import com.google.zxing.common.Collections;
import com.google.zxing.common.Comparator; import com.google.zxing.common.Comparator;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
@ -90,6 +91,10 @@ final class MultiFinderPatternFinder extends FinderPatternFinder {
super(image); super(image);
} }
MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
super(image, resultPointCallback);
}
/** /**
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
* those that have been detected at least {@link #CENTER_QUORUM} times, and whose module * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module

View file

@ -118,6 +118,14 @@ public abstract class AbstractOneDReader implements OneDReader {
for (int attempt = 0; attempt < 2; attempt++) { for (int attempt = 0; attempt < 2; attempt++) {
if (attempt == 1) { // trying again? if (attempt == 1) { // trying again?
row.reverse(); // reverse the row and continue 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 { try {
// Look for a barcode // Look for a barcode

View file

@ -17,9 +17,11 @@
package com.google.zxing.oned; package com.google.zxing.oned;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import java.util.Hashtable; 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) public final Result decodeRow(int rowNumber, BitArray row, Hashtable hints)
throws ReaderException { 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 { 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; StringBuffer result = decodeRowStringBuffer;
result.setLength(0); result.setLength(0);
int endStart = decodeMiddle(row, startGuardRange, result); int endStart = decodeMiddle(row, startGuardRange, result);
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(new ResultPoint(
endStart, rowNumber
));
}
int[] endRange = decodeEnd(row, endStart); 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 // 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. // spec might want more whitespace, but in practice this is the maximum we can count on.
int end = endRange[1]; int end = endRange[1];

View file

@ -68,7 +68,7 @@ public final class MultiFormatUPCEANReader extends AbstractOneDReader {
UPCEANReader reader = (UPCEANReader) readers.elementAt(i); UPCEANReader reader = (UPCEANReader) readers.elementAt(i);
Result result; Result result;
try { try {
result = reader.decodeRow(rowNumber, row, startGuardPattern); result = reader.decodeRow(rowNumber, row, startGuardPattern, hints);
} catch (ReaderException re) { } catch (ReaderException re) {
continue; continue;
} }

View file

@ -34,8 +34,9 @@ public final class UPCAReader implements UPCEANReader {
private final UPCEANReader ean13Reader = new EAN13Reader(); private final UPCEANReader ean13Reader = new EAN13Reader();
public Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange) throws ReaderException { public Result decodeRow(int rowNumber, BitArray row, int[] startGuardRange, Hashtable hints)
return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange)); throws ReaderException {
return maybeReturnResult(ean13Reader.decodeRow(rowNumber, row, startGuardRange, hints));
} }
public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException { public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException {

View file

@ -20,6 +20,8 @@ import com.google.zxing.ReaderException;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import java.util.Hashtable;
/** /**
* <p>This interfaces captures additional functionality that readers of * <p>This interfaces captures additional functionality that readers of
* UPC/EAN family of barcodes should expose.</p> * 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 * 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> * 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;
} }

View file

@ -17,6 +17,8 @@
package com.google.zxing.qrcode.detector; package com.google.zxing.qrcode.detector;
import com.google.zxing.ReaderException; 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.BitMatrix;
import java.util.Vector; import java.util.Vector;
@ -45,6 +47,7 @@ final class AlignmentPatternFinder {
private final int height; private final int height;
private final float moduleSize; private final float moduleSize;
private final int[] crossCheckStateCount; private final int[] crossCheckStateCount;
private final ResultPointCallback resultPointCallback;
/** /**
* <p>Creates a finder that will look in a portion of the whole image.</p> * <p>Creates a finder that will look in a portion of the whole image.</p>
@ -61,7 +64,8 @@ final class AlignmentPatternFinder {
int startY, int startY,
int width, int width,
int height, int height,
float moduleSize) { float moduleSize,
ResultPointCallback resultPointCallback) {
this.image = image; this.image = image;
this.possibleCenters = new Vector(5); this.possibleCenters = new Vector(5);
this.startX = startX; this.startX = startX;
@ -70,6 +74,7 @@ final class AlignmentPatternFinder {
this.height = height; this.height = height;
this.moduleSize = moduleSize; this.moduleSize = moduleSize;
this.crossCheckStateCount = new int[3]; this.crossCheckStateCount = new int[3];
this.resultPointCallback = resultPointCallback;
} }
/** /**
@ -262,7 +267,11 @@ final class AlignmentPatternFinder {
} }
} }
// Hadn't found this before; save it // Hadn't found this before; save it
possibleCenters.addElement(new AlignmentPattern(centerJ, centerI, estimatedModuleSize)); ResultPoint point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize);
possibleCenters.addElement(point);
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(point);
}
} }
return null; return null;
} }

View file

@ -16,8 +16,10 @@
package com.google.zxing.qrcode.detector; package com.google.zxing.qrcode.detector;
import com.google.zxing.DecodeHintType;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.DetectorResult; import com.google.zxing.common.DetectorResult;
import com.google.zxing.common.GridSampler; import com.google.zxing.common.GridSampler;
@ -34,6 +36,7 @@ import java.util.Hashtable;
public class Detector { public class Detector {
private final BitMatrix image; private final BitMatrix image;
private ResultPointCallback resultPointCallback;
public Detector(BitMatrix image) { public Detector(BitMatrix image) {
this.image = image; this.image = image;
@ -62,7 +65,10 @@ public class Detector {
*/ */
public DetectorResult detect(Hashtable hints) throws ReaderException { 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); FinderPatternInfo info = finder.find(hints);
return processFinderPatternInfo(info); return processFinderPatternInfo(info);
@ -347,7 +353,8 @@ public class Detector {
alignmentAreaTopY, alignmentAreaTopY,
alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaRightX - alignmentAreaLeftX,
alignmentAreaBottomY - alignmentAreaTopY, alignmentAreaBottomY - alignmentAreaTopY,
overallEstModuleSize); overallEstModuleSize,
resultPointCallback);
return alignmentFinder.find(); return alignmentFinder.find();
} }

View file

@ -19,6 +19,7 @@ package com.google.zxing.qrcode.detector;
import com.google.zxing.DecodeHintType; import com.google.zxing.DecodeHintType;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.ResultPoint; import com.google.zxing.ResultPoint;
import com.google.zxing.ResultPointCallback;
import com.google.zxing.common.Collections; import com.google.zxing.common.Collections;
import com.google.zxing.common.Comparator; import com.google.zxing.common.Comparator;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
@ -45,6 +46,7 @@ public class FinderPatternFinder {
private final Vector possibleCenters; private final Vector possibleCenters;
private boolean hasSkipped; private boolean hasSkipped;
private final int[] crossCheckStateCount; private final int[] crossCheckStateCount;
private final ResultPointCallback resultPointCallback;
/** /**
* <p>Creates a finder that will search the image for three finder patterns.</p> * <p>Creates a finder that will search the image for three finder patterns.</p>
@ -52,9 +54,14 @@ public class FinderPatternFinder {
* @param image image to search * @param image image to search
*/ */
public FinderPatternFinder(BitMatrix image) { public FinderPatternFinder(BitMatrix image) {
this(image, null);
}
public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) {
this.image = image; this.image = image;
this.possibleCenters = new Vector(); this.possibleCenters = new Vector();
this.crossCheckStateCount = new int[5]; this.crossCheckStateCount = new int[5];
this.resultPointCallback = resultPointCallback;
} }
protected BitMatrix getImage() { protected BitMatrix getImage() {
@ -401,7 +408,11 @@ public class FinderPatternFinder {
} }
} }
if (!found) { if (!found) {
possibleCenters.addElement(new FinderPattern(centerJ, centerI, estimatedModuleSize)); ResultPoint point = new FinderPattern(centerJ, centerI, estimatedModuleSize);
possibleCenters.addElement(point);
if (resultPointCallback != null) {
resultPointCallback.foundPossibleResultPoint(point);
}
} }
return true; return true;
} }