On scan we pass a grayscale image of the scanned image, which is used as a thumbnail. These days this is getting to be 2MB of data to bundle to copy around in memory. Instead scale by 50% and JPEG-encode. This saves some memory and paves the way for later perhaps saving the image data with history.

git-svn-id: https://zxing.googlecode.com/svn/trunk@2561 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2013-01-19 20:48:03 +00:00
parent 434a1bb6c5
commit 90e1822928
5 changed files with 61 additions and 26 deletions

View file

@ -399,9 +399,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
* A valid barcode has been found, so give an indication of success and show the results. * A valid barcode has been found, so give an indication of success and show the results.
* *
* @param rawResult The contents of the barcode. * @param rawResult The contents of the barcode.
* @param scaleFactor amount by which thumbnail was scaled
* @param barcode A greyscale bitmap of the camera data which was decoded. * @param barcode A greyscale bitmap of the camera data which was decoded.
*/ */
public void handleDecode(Result rawResult, Bitmap barcode) { public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
inactivityTimer.onActivity(); inactivityTimer.onActivity();
lastResult = rawResult; lastResult = rawResult;
ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult); ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
@ -411,7 +412,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
historyManager.addHistoryItem(rawResult, resultHandler); historyManager.addHistoryItem(rawResult, resultHandler);
// Then not from history, so beep/vibrate and we have an image to draw on // Then not from history, so beep/vibrate and we have an image to draw on
beepManager.playBeepSoundAndVibrate(); beepManager.playBeepSoundAndVibrate();
drawResultPoints(barcode, rawResult); drawResultPoints(barcode, scaleFactor, rawResult);
} }
switch (source) { switch (source) {
@ -445,9 +446,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
* Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode. * Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
* *
* @param barcode A bitmap of the captured image. * @param barcode A bitmap of the captured image.
* @param scaleFactor amount by which thumbnail was scaled
* @param rawResult The decoded results which contains the points to draw. * @param rawResult The decoded results which contains the points to draw.
*/ */
private void drawResultPoints(Bitmap barcode, Result rawResult) { private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
ResultPoint[] points = rawResult.getResultPoints(); ResultPoint[] points = rawResult.getResultPoints();
if (points != null && points.length > 0) { if (points != null && points.length > 0) {
Canvas canvas = new Canvas(barcode); Canvas canvas = new Canvas(barcode);
@ -455,24 +457,28 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
paint.setColor(getResources().getColor(R.color.result_points)); paint.setColor(getResources().getColor(R.color.result_points));
if (points.length == 2) { if (points.length == 2) {
paint.setStrokeWidth(4.0f); paint.setStrokeWidth(4.0f);
drawLine(canvas, paint, points[0], points[1]); drawLine(canvas, paint, points[0], points[1], scaleFactor);
} else if (points.length == 4 && } else if (points.length == 4 &&
(rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A || (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) { rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
// Hacky special case -- draw two lines, for the barcode and metadata // Hacky special case -- draw two lines, for the barcode and metadata
drawLine(canvas, paint, points[0], points[1]); drawLine(canvas, paint, points[0], points[1], scaleFactor);
drawLine(canvas, paint, points[2], points[3]); drawLine(canvas, paint, points[2], points[3], scaleFactor);
} else { } else {
paint.setStrokeWidth(10.0f); paint.setStrokeWidth(10.0f);
for (ResultPoint point : points) { for (ResultPoint point : points) {
canvas.drawPoint(point.getX(), point.getY(), paint); canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
} }
} }
} }
} }
private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b) { private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), paint); canvas.drawLine(scaleFactor * a.getX(),
scaleFactor * a.getY(),
scaleFactor * b.getX(),
scaleFactor * b.getY(),
paint);
} }
// Put up our own UI for how to handle the decoded contents. // Put up our own UI for how to handle the decoded contents.

View file

@ -19,6 +19,7 @@ package com.google.zxing.client.android;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.graphics.BitmapFactory;
import android.provider.Browser; import android.provider.Browser;
import com.google.zxing.BarcodeFormat; import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result; import com.google.zxing.Result;
@ -82,9 +83,18 @@ public final class CaptureActivityHandler extends Handler {
Log.d(TAG, "Got decode succeeded message"); Log.d(TAG, "Got decode succeeded message");
state = State.SUCCESS; state = State.SUCCESS;
Bundle bundle = message.getData(); Bundle bundle = message.getData();
Bitmap barcode = bundle == null ? null : Bitmap barcode = null;
(Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP); float scaleFactor = 1.0f;
activity.handleDecode((Result) message.obj, barcode); if (bundle != null) {
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
if (compressedBitmap != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, options);
}
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
}
activity.handleDecode((Result) message.obj, barcode, scaleFactor);
break; break;
case R.id.decode_failed: case R.id.decode_failed:
// We're decoding as fast as possible, so when one decode fails, start another. // We're decoding as fast as possible, so when one decode fails, start another.

View file

@ -19,7 +19,6 @@ package com.google.zxing.client.android;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import com.google.zxing.BinaryBitmap; import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType; import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
@ -32,6 +31,7 @@ import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.Log; import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.util.Map; import java.util.Map;
final class DecodeHandler extends Handler { final class DecodeHandler extends Handler {
@ -95,8 +95,7 @@ final class DecodeHandler extends Handler {
if (handler != null) { if (handler != null) {
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult); Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
Bitmap grayscaleBitmap = toBitmap(source, source.renderCroppedGreyscaleBitmap()); bundleThumbnail(source, bundle);
bundle.putParcelable(DecodeThread.BARCODE_BITMAP, grayscaleBitmap);
message.setData(bundle); message.setData(bundle);
message.sendToTarget(); message.sendToTarget();
} }
@ -108,12 +107,15 @@ final class DecodeHandler extends Handler {
} }
} }
private static Bitmap toBitmap(LuminanceSource source, int[] pixels) { private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
int width = source.getWidth(); int[] pixels = source.renderThumbnail();
int height = source.getHeight(); int width = source.getThumbnailWidth();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); int height = source.getThumbnailHeight();
bitmap.setPixels(pixels, 0, width, 0, 0, width, height); Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
return bitmap; ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());
} }
} }

View file

@ -39,6 +39,7 @@ import java.util.concurrent.CountDownLatch;
final class DecodeThread extends Thread { final class DecodeThread extends Thread {
public static final String BARCODE_BITMAP = "barcode_bitmap"; public static final String BARCODE_BITMAP = "barcode_bitmap";
public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
private final CaptureActivity activity; private final CaptureActivity activity;
private final Map<DecodeHintType,Object> hints; private final Map<DecodeHintType,Object> hints;

View file

@ -28,6 +28,8 @@ package com.google.zxing;
*/ */
public final class PlanarYUVLuminanceSource extends LuminanceSource { public final class PlanarYUVLuminanceSource extends LuminanceSource {
private static final int THUMBNAIL_SCALE_FACTOR = 2;
private final byte[] yuvData; private final byte[] yuvData;
private final int dataWidth; private final int dataWidth;
private final int dataHeight; private final int dataHeight;
@ -120,9 +122,9 @@ public final class PlanarYUVLuminanceSource extends LuminanceSource {
false); false);
} }
public int[] renderCroppedGreyscaleBitmap() { public int[] renderThumbnail() {
int width = getWidth(); int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
int height = getHeight(); int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
int[] pixels = new int[width * height]; int[] pixels = new int[width * height];
byte[] yuv = yuvData; byte[] yuv = yuvData;
int inputOffset = top * dataWidth + left; int inputOffset = top * dataWidth + left;
@ -130,14 +132,28 @@ public final class PlanarYUVLuminanceSource extends LuminanceSource {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
int outputOffset = y * width; int outputOffset = y * width;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
int grey = yuv[inputOffset + x] & 0xff; int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101); pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
} }
inputOffset += dataWidth; inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
} }
return pixels; return pixels;
} }
/**
* @return width of image from {@link #renderThumbnail()}
*/
public int getThumbnailWidth() {
return getWidth() / THUMBNAIL_SCALE_FACTOR;
}
/**
* @return height of image from {@link #renderThumbnail()}
*/
public int getThumbnailHeight() {
return getHeight() / THUMBNAIL_SCALE_FACTOR;
}
private void reverseHorizontal(int width, int height) { private void reverseHorizontal(int width, int height) {
byte[] yuvData = this.yuvData; byte[] yuvData = this.yuvData;
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) { for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {