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 MAX_ZOOM = 200;
private static final long FOCUS_TIME_MS = 1000L;
private static final long FOCUS_TIME_MS = 750L;
private AdvancedMultimediaManager() {
// 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,
* so this implementation computes luminance is a function of a red, green and blue components as
* follows:
*
* <code>Y = 0.299R + 0.587G + 0.114B</code>
*
* where R, G, and B are values in [0,1].
* An optimized approximation of a more proper conversion from RGB to luminance which
* only uses shifts. See BufferedImageMonochromeBitmapSource for an original version.
*/
private static int computeRGBLuminance(int pixel) {
// Coefficients add up to 1024 to make the divide into a fast shift
return (306 * ((pixel >> 16) & 0xFF) +
601 * ((pixel >> 8) & 0xFF) +
117 * (pixel & 0xFF)) >> 10;
// Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
// the multiplies can be implemented as shifts.
//
// 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;
/**
* Thread which does the work of capturing a frame and decoding it.
*
* @author Sean Owen (srowen@google.com)
*/
final class SnapshotThread extends Thread {
private static SnapshotThread currentThread;
private final ZXingMIDlet zXingMIDlet;
private final Object waitLock;
private boolean done;
private SnapshotThread(ZXingMIDlet zXingMIDlet) {
SnapshotThread(ZXingMIDlet zXingMIDlet) {
this.zXingMIDlet = zXingMIDlet;
waitLock = new Object();
done = false;
}
static synchronized void startThread(ZXingMIDlet zXingMIDlet) {
if (currentThread == null) {
currentThread = new SnapshotThread(zXingMIDlet);
currentThread.start();
void continueRun() {
synchronized (waitLock) {
waitLock.notifyAll();
}
}
private void waitForSignal() {
synchronized (waitLock) {
try {
waitLock.wait();
} catch (InterruptedException ie) {
// continue
}
}
}
void stop() {
done = true;
continueRun();
}
public void run() {
Player player = zXingMIDlet.getPlayer();
try {
AdvancedMultimediaManager.setFocus(player);
do {
waitForSignal();
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) {
// continue
zXingMIDlet.showError(me);
} catch (RuntimeException re) {
zXingMIDlet.showError(re);
}
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) {
zXingMIDlet.showError(me);
} catch (RuntimeException re) {
zXingMIDlet.showError(re);
} finally {
try {
player.start();
} catch (MediaException me) {
// continue
}
currentThread = null;
}
} while (!done);
}
private byte[] takeSnapshot() throws MediaException {

View file

@ -23,6 +23,9 @@ import javax.microedition.lcdui.Displayable;
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)
*/
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 final ZXingMIDlet zXingMIDlet;
private final SnapshotThread snapshotThread;
VideoCanvas(ZXingMIDlet zXingMIDlet) {
this.zXingMIDlet = zXingMIDlet;
addCommand(exit);
setCommandListener(this);
snapshotThread = new SnapshotThread(zXingMIDlet);
snapshotThread.start();
}
protected void paint(Graphics graphics) {
@ -44,12 +50,16 @@ final class VideoCanvas extends Canvas implements CommandListener {
protected void keyPressed(int keyCode) {
// Any valid game key will trigger a capture
if (getGameAction(keyCode) != 0) {
SnapshotThread.startThread(zXingMIDlet);
snapshotThread.continueRun();
} else {
super.keyPressed(keyCode);
}
}
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();
}
}