<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>
}
};
+ ViewfinderView getViewfinderView() {
+ return viewfinderView;
+ }
+
public Handler getHandler() {
return 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;
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;
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) {
}
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);
}
/**
* 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);
}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
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
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) {
// 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
// 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);
}
}
resultBitmap = barcode;
invalidate();
}
+
+ public void addPossibleResultPoint(ResultPoint point) {
+ possibleResultPoints.add(point);
+ }
+
}
*/
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() {
}
--- /dev/null
+/*
+ * 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);
+
+}
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
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
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
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;
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];
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;
}
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 {
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>
* 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
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
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
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
this.height = height;\r
this.moduleSize = moduleSize;\r
this.crossCheckStateCount = new int[3];\r
+ this.resultPointCallback = resultPointCallback;\r
}\r
\r
/**\r
}\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
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;
public class Detector {
private final BitMatrix image;
+ private ResultPointCallback resultPointCallback;
public Detector(BitMatrix image) {
this.image = image;
*/
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);
alignmentAreaTopY,
alignmentAreaRightX - alignmentAreaLeftX,
alignmentAreaBottomY - alignmentAreaTopY,
- overallEstModuleSize);
+ overallEstModuleSize,
+ resultPointCallback);
return alignmentFinder.find();
}
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
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
* @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
}\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