Various improvements to decode speed and efficiency of J2ME client

git-svn-id: https://zxing.googlecode.com/svn/trunk@257 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2008-03-10 16:18:42 +00:00
parent 4abc8aecec
commit 8c03ed9301
4 changed files with 73 additions and 47 deletions

View file

@ -37,7 +37,7 @@ final class AdvancedMultimediaManager {
private static final int NO_ZOOM = 100; private static final int NO_ZOOM = 100;
private static final int MAX_ZOOM = 200; private static final int MAX_ZOOM = 200;
private static final long FOCUS_TIME_MS = 1000L; private static final long FOCUS_TIME_MS = 750L;
private AdvancedMultimediaManager() { private AdvancedMultimediaManager() {
// do nothing // do nothing

View file

@ -117,19 +117,27 @@ public final class LCDUIImageMonochromeBitmapSource implements MonochromeBitmapS
} }
/** /**
* Extracts luminance from a pixel from this source. By default, the source is assumed to use RGB, * An optimized approximation of a more proper conversion from RGB to luminance which
* so this implementation computes luminance is a function of a red, green and blue components as * only uses shifts. See BufferedImageMonochromeBitmapSource for an original version.
* follows:
*
* <code>Y = 0.299R + 0.587G + 0.114B</code>
*
* where R, G, and B are values in [0,1].
*/ */
private static int computeRGBLuminance(int pixel) { private static int computeRGBLuminance(int pixel) {
// Coefficients add up to 1024 to make the divide into a fast shift // Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
return (306 * ((pixel >> 16) & 0xFF) + // the multiplies can be implemented as shifts.
601 * ((pixel >> 8) & 0xFF) + //
117 * (pixel & 0xFF)) >> 10; // Really, it's:
//
// return ((((pixel >> 16) & 0xFF) << 8) +
// (((pixel >> 8) & 0xFF) << 9) +
// (( pixel & 0xFF) << 8)) >> 10;
//
// That is, we're replacing the coefficients in the original with powers of two,
// which can be implemented as shifts, even though changing the coefficients slightly
// corrupts the conversion. Not significant for our purposes.
//
// But we can get even cleverer and eliminate a few shifts:
return (((pixel & 0x00FF0000) >> 8) +
((pixel & 0x0000FF00) << 1) +
((pixel & 0x000000FF) << 8)) >> 10;
} }
} }

View file

@ -28,56 +28,64 @@ import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl; import javax.microedition.media.control.VideoControl;
/** /**
* Thread which does the work of capturing a frame and decoding it.
*
* @author Sean Owen (srowen@google.com) * @author Sean Owen (srowen@google.com)
*/ */
final class SnapshotThread extends Thread { final class SnapshotThread extends Thread {
private static SnapshotThread currentThread;
private final ZXingMIDlet zXingMIDlet; private final ZXingMIDlet zXingMIDlet;
private final Object waitLock;
private boolean done;
private SnapshotThread(ZXingMIDlet zXingMIDlet) { SnapshotThread(ZXingMIDlet zXingMIDlet) {
this.zXingMIDlet = zXingMIDlet; this.zXingMIDlet = zXingMIDlet;
waitLock = new Object();
done = false;
} }
static synchronized void startThread(ZXingMIDlet zXingMIDlet) { void continueRun() {
if (currentThread == null) { synchronized (waitLock) {
currentThread = new SnapshotThread(zXingMIDlet); waitLock.notifyAll();
currentThread.start();
} }
} }
private void waitForSignal() {
synchronized (waitLock) {
try {
waitLock.wait();
} catch (InterruptedException ie) {
// continue
}
}
}
void stop() {
done = true;
continueRun();
}
public void run() { public void run() {
Player player = zXingMIDlet.getPlayer(); Player player = zXingMIDlet.getPlayer();
try { do {
AdvancedMultimediaManager.setFocus(player); waitForSignal();
try { try {
player.stop(); AdvancedMultimediaManager.setFocus(player);
byte[] snapshot = takeSnapshot();
Image capturedImage = Image.createImage(snapshot, 0, snapshot.length);
MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
Reader reader = new MultiFormatReader();
Result result = reader.decode(source);
zXingMIDlet.handleDecodedText(result.getText());
} catch (ReaderException re) {
// Show a friendlier message on a mere failure to read the barcode
zXingMIDlet.showError("Sorry, no barcode was found.");
} catch (MediaException me) { } catch (MediaException me) {
// continue zXingMIDlet.showError(me);
} catch (RuntimeException re) {
zXingMIDlet.showError(re);
} }
byte[] snapshot = takeSnapshot(); } while (!done);
Image capturedImage = Image.createImage(snapshot, 0, snapshot.length);
MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
Reader reader = new MultiFormatReader();
Result result = reader.decode(source);
zXingMIDlet.handleDecodedText(result.getText());
} catch (ReaderException re) {
// Show a friendlier message on a mere failure to read the barcode
zXingMIDlet.showError("Sorry, no barcode was found.");
} catch (MediaException me) {
zXingMIDlet.showError(me);
} catch (RuntimeException re) {
zXingMIDlet.showError(re);
} finally {
try {
player.start();
} catch (MediaException me) {
// continue
}
currentThread = null;
}
} }
private byte[] takeSnapshot() throws MediaException { private byte[] takeSnapshot() throws MediaException {

View file

@ -23,6 +23,9 @@ import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Graphics;
/** /**
* The main {@link Canvas} onto which the camera's field of view is painted.
* This class manages decoding via {@link SnapshotThread}.
*
* @author Sean Owen (srowen@google.com) * @author Sean Owen (srowen@google.com)
*/ */
final class VideoCanvas extends Canvas implements CommandListener { final class VideoCanvas extends Canvas implements CommandListener {
@ -30,11 +33,14 @@ final class VideoCanvas extends Canvas implements CommandListener {
private static final Command exit = new Command("Exit", Command.EXIT, 1); private static final Command exit = new Command("Exit", Command.EXIT, 1);
private final ZXingMIDlet zXingMIDlet; private final ZXingMIDlet zXingMIDlet;
private final SnapshotThread snapshotThread;
VideoCanvas(ZXingMIDlet zXingMIDlet) { VideoCanvas(ZXingMIDlet zXingMIDlet) {
this.zXingMIDlet = zXingMIDlet; this.zXingMIDlet = zXingMIDlet;
addCommand(exit); addCommand(exit);
setCommandListener(this); setCommandListener(this);
snapshotThread = new SnapshotThread(zXingMIDlet);
snapshotThread.start();
} }
protected void paint(Graphics graphics) { protected void paint(Graphics graphics) {
@ -44,12 +50,16 @@ final class VideoCanvas extends Canvas implements CommandListener {
protected void keyPressed(int keyCode) { protected void keyPressed(int keyCode) {
// Any valid game key will trigger a capture // Any valid game key will trigger a capture
if (getGameAction(keyCode) != 0) { if (getGameAction(keyCode) != 0) {
SnapshotThread.startThread(zXingMIDlet); snapshotThread.continueRun();
} else {
super.keyPressed(keyCode);
} }
} }
public void commandAction(Command command, Displayable displayable) { public void commandAction(Command command, Displayable displayable) {
if (command.getCommandType() == Command.EXIT || command.getCommandType() == Command.STOP) { int type = command.getCommandType();
if (type == Command.EXIT || type == Command.STOP || type == Command.BACK || type == Command.CANCEL) {
snapshotThread.stop();
zXingMIDlet.stop(); zXingMIDlet.stop();
} }
} }