mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-05 05:46:44 +00:00
reduce java-based cpu usage when scanning
This commit is contained in:
parent
e524396aaf
commit
d635815607
3 changed files with 81 additions and 29 deletions
|
@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.control;
|
||||||
import com.github.sarxos.webcam.WebcamEvent;
|
import com.github.sarxos.webcam.WebcamEvent;
|
||||||
import com.github.sarxos.webcam.WebcamListener;
|
import com.github.sarxos.webcam.WebcamListener;
|
||||||
import com.github.sarxos.webcam.WebcamResolution;
|
import com.github.sarxos.webcam.WebcamResolution;
|
||||||
|
import com.github.sarxos.webcam.WebcamUpdater;
|
||||||
import com.sparrowwallet.drongo.ExtendedKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.OutputDescriptor;
|
import com.sparrowwallet.drongo.OutputDescriptor;
|
||||||
|
@ -39,6 +40,7 @@ import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.util.Duration;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
import org.controlsfx.tools.Borders;
|
import org.controlsfx.tools.Borders;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -67,6 +69,7 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
|
|
||||||
private static final Pattern PART_PATTERN = Pattern.compile("p(\\d+)of(\\d+) (.+)");
|
private static final Pattern PART_PATTERN = Pattern.compile("p(\\d+)of(\\d+) (.+)");
|
||||||
|
|
||||||
|
private static final int SCAN_PERIOD_MILLIS = 100;
|
||||||
private final ObjectProperty<WebcamResolution> webcamResolutionProperty = new SimpleObjectProperty<>(WebcamResolution.VGA);
|
private final ObjectProperty<WebcamResolution> webcamResolutionProperty = new SimpleObjectProperty<>(WebcamResolution.VGA);
|
||||||
|
|
||||||
private final DoubleProperty percentComplete = new SimpleDoubleProperty(0.0);
|
private final DoubleProperty percentComplete = new SimpleDoubleProperty(0.0);
|
||||||
|
@ -79,7 +82,9 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
webcamResolutionProperty.set(WebcamResolution.HD);
|
webcamResolutionProperty.set(WebcamResolution.HD);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.webcamService = new WebcamService(webcamResolutionProperty.get(), new QRScanListener());
|
this.webcamService = new WebcamService(webcamResolutionProperty.get(), new QRScanListener(), new ScanDelayCalculator());
|
||||||
|
webcamService.setPeriod(Duration.millis(SCAN_PERIOD_MILLIS));
|
||||||
|
webcamService.setRestartOnFailure(false);
|
||||||
WebcamView webcamView = new WebcamView(webcamService);
|
WebcamView webcamView = new WebcamView(webcamService);
|
||||||
|
|
||||||
final DialogPane dialogPane = new QRScanDialogPane();
|
final DialogPane dialogPane = new QRScanDialogPane();
|
||||||
|
@ -703,4 +708,10 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ScanDelayCalculator implements WebcamUpdater.DelayCalculator {
|
||||||
|
public long calculateDelay(long snapshotDuration, double deviceFps) {
|
||||||
|
return Math.max(SCAN_PERIOD_MILLIS - snapshotDuration, 0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.control;
|
||||||
import com.github.sarxos.webcam.Webcam;
|
import com.github.sarxos.webcam.Webcam;
|
||||||
import com.github.sarxos.webcam.WebcamListener;
|
import com.github.sarxos.webcam.WebcamListener;
|
||||||
import com.github.sarxos.webcam.WebcamResolution;
|
import com.github.sarxos.webcam.WebcamResolution;
|
||||||
|
import com.github.sarxos.webcam.WebcamUpdater;
|
||||||
import com.google.zxing.*;
|
import com.google.zxing.*;
|
||||||
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||||
import com.google.zxing.common.HybridBinarizer;
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
@ -10,7 +11,7 @@ import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
@ -19,16 +20,24 @@ import java.awt.image.BufferedImage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class WebcamService extends Service<Image> {
|
public class WebcamService extends ScheduledService<Image> {
|
||||||
private WebcamResolution resolution;
|
private WebcamResolution resolution;
|
||||||
private final WebcamListener listener;
|
private final WebcamListener listener;
|
||||||
private BooleanProperty opening = new SimpleBooleanProperty(false);
|
private final WebcamUpdater.DelayCalculator delayCalculator;
|
||||||
|
private final BooleanProperty opening = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
private final ObjectProperty<Result> resultProperty = new SimpleObjectProperty<>(null);
|
private final ObjectProperty<Result> resultProperty = new SimpleObjectProperty<>(null);
|
||||||
|
|
||||||
public WebcamService(WebcamResolution resolution, WebcamListener listener) {
|
private static final int QR_SAMPLE_PERIOD_MILLIS = 400;
|
||||||
|
|
||||||
|
private Webcam cam;
|
||||||
|
private long lastQrSampleTime;
|
||||||
|
|
||||||
|
public WebcamService(WebcamResolution resolution, WebcamListener listener, WebcamUpdater.DelayCalculator delayCalculator) {
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
this.delayCalculator = delayCalculator;
|
||||||
|
this.lastQrSampleTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,35 +45,52 @@ public class WebcamService extends Service<Image> {
|
||||||
return new Task<Image>() {
|
return new Task<Image>() {
|
||||||
@Override
|
@Override
|
||||||
protected Image call() throws Exception {
|
protected Image call() throws Exception {
|
||||||
Webcam cam = Webcam.getWebcams(1, TimeUnit.MINUTES).get(0);
|
|
||||||
try {
|
try {
|
||||||
cam.setCustomViewSizes(resolution.getSize());
|
if(cam == null) {
|
||||||
cam.setViewSize(resolution.getSize());
|
cam = Webcam.getWebcams(1, TimeUnit.MINUTES).get(0);
|
||||||
if(!Arrays.asList(cam.getWebcamListeners()).contains(listener)) {
|
cam.setCustomViewSizes(resolution.getSize());
|
||||||
cam.addWebcamListener(listener);
|
cam.setViewSize(resolution.getSize());
|
||||||
|
if(!Arrays.asList(cam.getWebcamListeners()).contains(listener)) {
|
||||||
|
cam.addWebcamListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
opening.set(true);
|
||||||
|
cam.open(true, delayCalculator);
|
||||||
|
opening.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
opening.set(true);
|
BufferedImage bimg = cam.getImage();
|
||||||
cam.open();
|
Image image = SwingFXUtils.toFXImage(bimg, null);
|
||||||
opening.set(false);
|
updateValue(image);
|
||||||
while(!isCancelled()) {
|
|
||||||
if(cam.isImageNew()) {
|
if(System.currentTimeMillis() > (lastQrSampleTime + QR_SAMPLE_PERIOD_MILLIS)) {
|
||||||
BufferedImage bimg = cam.getImage();
|
readQR(bimg);
|
||||||
updateValue(SwingFXUtils.toFXImage(bimg, null));
|
lastQrSampleTime = System.currentTimeMillis();
|
||||||
readQR(bimg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return getValue();
|
|
||||||
|
return image;
|
||||||
} finally {
|
} finally {
|
||||||
opening.set(false);
|
opening.set(false);
|
||||||
if(!cam.close()) {
|
|
||||||
cam.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
cam = null;
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel() {
|
||||||
|
if(cam != null && !cam.close()) {
|
||||||
|
cam.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
private void readQR(BufferedImage bufferedImage) {
|
private void readQR(BufferedImage bufferedImage) {
|
||||||
LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
|
LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
|
||||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -14,7 +17,9 @@ public class WebcamView {
|
||||||
private final WebcamService service;
|
private final WebcamService service;
|
||||||
private final Region view;
|
private final Region view;
|
||||||
|
|
||||||
private final Label statusPlaceholder ;
|
private final Label statusPlaceholder;
|
||||||
|
|
||||||
|
private final ObjectProperty<Image> imageProperty = new SimpleObjectProperty<>(null);
|
||||||
|
|
||||||
public WebcamView(WebcamService service) {
|
public WebcamView(WebcamService service) {
|
||||||
this.service = service ;
|
this.service = service ;
|
||||||
|
@ -23,22 +28,32 @@ public class WebcamView {
|
||||||
// make the cam behave like a mirror:
|
// make the cam behave like a mirror:
|
||||||
imageView.setScaleX(-1);
|
imageView.setScaleX(-1);
|
||||||
|
|
||||||
|
service.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(newValue != null) {
|
||||||
|
imageProperty.set(newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.statusPlaceholder = new Label();
|
this.statusPlaceholder = new Label();
|
||||||
this.view = new Region() {
|
this.view = new Region() {
|
||||||
{
|
{
|
||||||
service.stateProperty().addListener((obs, oldState, newState) -> {
|
service.stateProperty().addListener((obs, oldState, newState) -> {
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case READY:
|
case READY:
|
||||||
statusPlaceholder.setText("Initializing");
|
if(imageProperty.get() == null) {
|
||||||
getChildren().setAll(statusPlaceholder);
|
statusPlaceholder.setText("Initializing");
|
||||||
|
getChildren().setAll(statusPlaceholder);
|
||||||
|
}
|
||||||
break ;
|
break ;
|
||||||
case SCHEDULED:
|
case SCHEDULED:
|
||||||
statusPlaceholder.setText("Waiting");
|
if(imageProperty.get() == null) {
|
||||||
getChildren().setAll(statusPlaceholder);
|
statusPlaceholder.setText("Waiting");
|
||||||
|
getChildren().setAll(statusPlaceholder);
|
||||||
|
}
|
||||||
break ;
|
break ;
|
||||||
case RUNNING:
|
case RUNNING:
|
||||||
imageView.imageProperty().unbind();
|
imageView.imageProperty().unbind();
|
||||||
imageView.imageProperty().bind(service.valueProperty());
|
imageView.imageProperty().bind(imageProperty);
|
||||||
getChildren().setAll(imageView);
|
getChildren().setAll(imageView);
|
||||||
break ;
|
break ;
|
||||||
case CANCELLED:
|
case CANCELLED:
|
||||||
|
|
Loading…
Reference in a new issue