mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
support capturing using additional webcam resolutions of fhd and uhd4k
This commit is contained in:
parent
bd5af560ff
commit
3e197eb310
7 changed files with 173 additions and 103 deletions
|
|
@ -379,7 +379,7 @@ public class AppController implements Initializable {
|
||||||
openWalletsInNewWindows.selectedProperty().bindBidirectional(openWalletsInNewWindowsProperty);
|
openWalletsInNewWindows.selectedProperty().bindBidirectional(openWalletsInNewWindowsProperty);
|
||||||
hideEmptyUsedAddressesProperty.set(Config.get().isHideEmptyUsedAddresses());
|
hideEmptyUsedAddressesProperty.set(Config.get().isHideEmptyUsedAddresses());
|
||||||
hideEmptyUsedAddresses.selectedProperty().bindBidirectional(hideEmptyUsedAddressesProperty);
|
hideEmptyUsedAddresses.selectedProperty().bindBidirectional(hideEmptyUsedAddressesProperty);
|
||||||
useHdCameraResolutionProperty.set(Config.get().isHdCapture());
|
useHdCameraResolutionProperty.set(Config.get().getWebcamResolution() == null || Config.get().getWebcamResolution().isWidescreenAspect());
|
||||||
useHdCameraResolution.selectedProperty().bindBidirectional(useHdCameraResolutionProperty);
|
useHdCameraResolution.selectedProperty().bindBidirectional(useHdCameraResolutionProperty);
|
||||||
mirrorCameraImageProperty.set(Config.get().isMirrorCapture());
|
mirrorCameraImageProperty.set(Config.get().isMirrorCapture());
|
||||||
mirrorCameraImage.selectedProperty().bindBidirectional(mirrorCameraImageProperty);
|
mirrorCameraImage.selectedProperty().bindBidirectional(mirrorCameraImageProperty);
|
||||||
|
|
@ -944,7 +944,11 @@ public class AppController implements Initializable {
|
||||||
|
|
||||||
public void useHdCameraResolution(ActionEvent event) {
|
public void useHdCameraResolution(ActionEvent event) {
|
||||||
CheckMenuItem item = (CheckMenuItem)event.getSource();
|
CheckMenuItem item = (CheckMenuItem)event.getSource();
|
||||||
Config.get().setHdCapture(item.isSelected());
|
if(Config.get().getWebcamResolution().isStandardAspect() && item.isSelected()) {
|
||||||
|
Config.get().setWebcamResolution(WebcamResolution.HD);
|
||||||
|
} else if(Config.get().getWebcamResolution().isWidescreenAspect() && !item.isSelected()) {
|
||||||
|
Config.get().setWebcamResolution(WebcamResolution.VGA);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mirrorCameraImage(ActionEvent event) {
|
public void mirrorCameraImage(ActionEvent event) {
|
||||||
|
|
@ -3150,7 +3154,7 @@ public class AppController implements Initializable {
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void webcamResolutionChanged(WebcamResolutionChangedEvent event) {
|
public void webcamResolutionChanged(WebcamResolutionChangedEvent event) {
|
||||||
useHdCameraResolutionProperty.set(event.isHdResolution());
|
useHdCameraResolutionProperty.set(event.getResolution().isWidescreenAspect());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,6 @@ public class SparrowDesktop extends Application {
|
||||||
Config.get().setServerType(ServerType.ELECTRUM_SERVER);
|
Config.get().setServerType(ServerType.ELECTRUM_SERVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Config.get().getHdCapture() == null && OsType.getCurrent() == OsType.MACOS) {
|
|
||||||
Config.get().setHdCapture(Boolean.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.setProperty(Wallet.ALLOW_DERIVATIONS_MATCHING_OTHER_SCRIPT_TYPES_PROPERTY, Boolean.toString(!Config.get().isValidateDerivationPaths()));
|
System.setProperty(Wallet.ALLOW_DERIVATIONS_MATCHING_OTHER_SCRIPT_TYPES_PROPERTY, Boolean.toString(!Config.get().isValidateDerivationPaths()));
|
||||||
System.setProperty(Wallet.ALLOW_DERIVATIONS_MATCHING_OTHER_NETWORKS_PROPERTY, Boolean.toString(!Config.get().isValidateDerivationPaths()));
|
System.setProperty(Wallet.ALLOW_DERIVATIONS_MATCHING_OTHER_NETWORKS_PROPERTY, Boolean.toString(!Config.get().isValidateDerivationPaths()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import com.sparrowwallet.hummingbird.registry.pathcomponent.PathComponent;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.WebcamResolutionChangedEvent;
|
import com.sparrowwallet.sparrow.event.WebcamResolutionChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.io.bbqr.BBQRDecoder;
|
import com.sparrowwallet.sparrow.io.bbqr.BBQRDecoder;
|
||||||
import com.sparrowwallet.sparrow.io.bbqr.BBQRException;
|
import com.sparrowwallet.sparrow.io.bbqr.BBQRException;
|
||||||
|
|
@ -39,13 +38,14 @@ import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.Insets;
|
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 javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
|
||||||
import org.controlsfx.tools.Borders;
|
import org.controlsfx.tools.Borders;
|
||||||
import org.openpnp.capture.CaptureDevice;
|
import org.openpnp.capture.CaptureDevice;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
@ -77,19 +77,22 @@ 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 static final int SCAN_PERIOD_MILLIS = 100;
|
||||||
private final ObjectProperty<WebcamResolution> webcamResolutionProperty = new SimpleObjectProperty<>(WebcamResolution.VGA);
|
private final ObjectProperty<CaptureDevice> webcamDeviceProperty = new SimpleObjectProperty<>();
|
||||||
|
private final ObjectProperty<WebcamResolution> webcamResolutionProperty = new SimpleObjectProperty<>(WebcamResolution.HD);
|
||||||
|
|
||||||
private final DoubleProperty percentComplete = new SimpleDoubleProperty(0.0);
|
private final DoubleProperty percentComplete = new SimpleDoubleProperty(0.0);
|
||||||
|
|
||||||
private final ObjectProperty<CaptureDevice> webcamDeviceProperty = new SimpleObjectProperty<>();
|
private final ObservableList<CaptureDevice> foundDevices = FXCollections.observableList(new ArrayList<>());
|
||||||
|
private final ObservableList<WebcamResolution> availableResolutions = FXCollections.observableList(new ArrayList<>());
|
||||||
|
private boolean postOpenUpdate;
|
||||||
|
|
||||||
public QRScanDialog() {
|
public QRScanDialog() {
|
||||||
this.urDecoder = new URDecoder();
|
this.urDecoder = new URDecoder();
|
||||||
this.legacyUrDecoder = new LegacyURDecoder();
|
this.legacyUrDecoder = new LegacyURDecoder();
|
||||||
this.bbqrDecoder = new BBQRDecoder();
|
this.bbqrDecoder = new BBQRDecoder();
|
||||||
|
|
||||||
if(Config.get().isHdCapture()) {
|
if(Config.get().getWebcamResolution() != null) {
|
||||||
webcamResolutionProperty.set(WebcamResolution.HD);
|
webcamResolutionProperty.set(Config.get().getWebcamResolution());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.webcamService = new WebcamService(webcamResolutionProperty.get(), null);
|
this.webcamService = new WebcamService(webcamResolutionProperty.get(), null);
|
||||||
|
|
@ -110,19 +113,35 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
progressBar.setPadding(new Insets(0, 10, 0, 10));
|
progressBar.setPadding(new Insets(0, 10, 0, 10));
|
||||||
progressBar.setPrefWidth(Integer.MAX_VALUE);
|
progressBar.setPrefWidth(Integer.MAX_VALUE);
|
||||||
progressBar.progressProperty().bind(percentComplete);
|
progressBar.progressProperty().bind(percentComplete);
|
||||||
webcamService.openingProperty().addListener((_, _, newValue) -> {
|
webcamService.openingProperty().addListener((_, _, opening) -> {
|
||||||
if(percentComplete.get() <= 0.0) {
|
if(percentComplete.get() <= 0.0) {
|
||||||
Platform.runLater(() -> percentComplete.set(newValue ? 0.0 : -1.0));
|
Platform.runLater(() -> percentComplete.set(opening ? 0.0 : -1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(opening) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
try {
|
||||||
|
postOpenUpdate = true;
|
||||||
|
List<CaptureDevice> newDevices = new ArrayList<>(webcamService.getDevices());
|
||||||
|
newDevices.removeAll(foundDevices);
|
||||||
|
foundDevices.addAll(newDevices);
|
||||||
|
foundDevices.removeIf(device -> !webcamService.getDevices().contains(device));
|
||||||
|
|
||||||
if(Config.get().getWebcamDevice() != null && webcamDeviceProperty.get() == null) {
|
if(Config.get().getWebcamDevice() != null && webcamDeviceProperty.get() == null) {
|
||||||
for(CaptureDevice device : webcamService.getFoundDevices()) {
|
for(CaptureDevice device : foundDevices) {
|
||||||
if(device.getName().equals(Config.get().getWebcamDevice())) {
|
if(device.getName().equals(Config.get().getWebcamDevice())) {
|
||||||
webcamDeviceProperty.set(device);
|
webcamDeviceProperty.set(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateList(availableResolutions, webcamService.getResolutions());
|
||||||
|
webcamResolutionProperty.set(webcamService.getResolution());
|
||||||
|
} finally {
|
||||||
|
postOpenUpdate = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
webcamService.closedProperty().addListener((_, _, closed) -> {
|
webcamService.closedProperty().addListener((_, _, closed) -> {
|
||||||
if(closed && webcamResolutionProperty.get() != null) {
|
if(closed && webcamResolutionProperty.get() != null) {
|
||||||
|
|
@ -148,12 +167,18 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
Platform.runLater(() -> setResult(new Result(exception)));
|
Platform.runLater(() -> setResult(new Result(exception)));
|
||||||
});
|
});
|
||||||
webcamService.start();
|
webcamService.start();
|
||||||
webcamResolutionProperty.addListener((_, _, newResolution) -> {
|
webcamResolutionProperty.addListener((_, oldResolution, newResolution) -> {
|
||||||
if(newResolution != null) {
|
if(newResolution != null) {
|
||||||
setHeight(newResolution == WebcamResolution.HD ? (getHeight() - 100) : (getHeight() + 100));
|
if(newResolution.isStandardAspect() && oldResolution.isWidescreenAspect()) {
|
||||||
EventManager.get().post(new WebcamResolutionChangedEvent(newResolution == WebcamResolution.HD));
|
setHeight(getHeight() + 100);
|
||||||
|
} else if(newResolution.isWidescreenAspect() && oldResolution.isStandardAspect()) {
|
||||||
|
setHeight(getHeight() - 100);
|
||||||
}
|
}
|
||||||
|
EventManager.get().post(new WebcamResolutionChangedEvent(newResolution));
|
||||||
|
}
|
||||||
|
if(newResolution == null || !postOpenUpdate) {
|
||||||
webcamService.cancel();
|
webcamService.cancel();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
webcamDeviceProperty.addListener((_, _, newValue) -> {
|
webcamDeviceProperty.addListener((_, _, newValue) -> {
|
||||||
Config.get().setWebcamDevice(newValue.getName());
|
Config.get().setWebcamDevice(newValue.getName());
|
||||||
|
|
@ -163,9 +188,8 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
});
|
});
|
||||||
|
|
||||||
setOnCloseRequest(_ -> {
|
setOnCloseRequest(_ -> {
|
||||||
boolean isHdCapture = (webcamResolutionProperty.get() == WebcamResolution.HD);
|
if(webcamResolutionProperty.get() != null) {
|
||||||
if(Config.get().isHdCapture() != isHdCapture) {
|
Config.get().setWebcamResolution(webcamResolutionProperty.get());
|
||||||
Config.get().setHdCapture(isHdCapture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
|
@ -175,11 +199,11 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
});
|
});
|
||||||
|
|
||||||
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE);
|
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||||
final ButtonType hdButtonType = new javafx.scene.control.ButtonType("Use HD Capture", ButtonBar.ButtonData.LEFT);
|
final ButtonType deviceButtonType = new javafx.scene.control.ButtonType("Default Camera", ButtonBar.ButtonData.LEFT);
|
||||||
final ButtonType camButtonType = new javafx.scene.control.ButtonType("Default Camera", ButtonBar.ButtonData.HELP_2);
|
final ButtonType resolutionButtonType = new javafx.scene.control.ButtonType("Resolution", ButtonBar.ButtonData.HELP_2);
|
||||||
dialogPane.getButtonTypes().addAll(hdButtonType, camButtonType, cancelButtonType);
|
dialogPane.getButtonTypes().addAll(deviceButtonType, resolutionButtonType, cancelButtonType);
|
||||||
dialogPane.setPrefWidth(646);
|
dialogPane.setPrefWidth(646);
|
||||||
dialogPane.setPrefHeight(webcamResolutionProperty.get() == WebcamResolution.HD ? 490 : 590);
|
dialogPane.setPrefHeight(webcamResolutionProperty.get().isWidescreenAspect() ? 490 : 590);
|
||||||
dialogPane.setMinHeight(dialogPane.getPrefHeight());
|
dialogPane.setMinHeight(dialogPane.getPrefHeight());
|
||||||
AppServices.moveToActiveWindowScreen(this);
|
AppServices.moveToActiveWindowScreen(this);
|
||||||
|
|
||||||
|
|
@ -690,23 +714,9 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
private class QRScanDialogPane extends DialogPane {
|
private class QRScanDialogPane extends DialogPane {
|
||||||
@Override
|
@Override
|
||||||
protected Node createButton(ButtonType buttonType) {
|
protected Node createButton(ButtonType buttonType) {
|
||||||
Node button = null;
|
Node button;
|
||||||
if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT) {
|
if(buttonType.getButtonData() == ButtonBar.ButtonData.LEFT) {
|
||||||
ToggleButton hd = new ToggleButton(buttonType.getText());
|
ComboBox<CaptureDevice> devicesCombo = new ComboBox<>(foundDevices);
|
||||||
hd.setSelected(webcamResolutionProperty.get() == WebcamResolution.HD);
|
|
||||||
hd.setGraphicTextGap(5);
|
|
||||||
setHdGraphic(hd, hd.isSelected());
|
|
||||||
|
|
||||||
final ButtonBar.ButtonData buttonData = buttonType.getButtonData();
|
|
||||||
ButtonBar.setButtonData(hd, buttonData);
|
|
||||||
hd.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
webcamResolutionProperty.set(newValue ? WebcamResolution.HD : WebcamResolution.VGA);
|
|
||||||
setHdGraphic(hd, newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
button = hd;
|
|
||||||
} else if(buttonType.getButtonData() == ButtonBar.ButtonData.HELP_2) {
|
|
||||||
ComboBox<CaptureDevice> devicesCombo = new ComboBox<>(webcamService.getFoundDevices());
|
|
||||||
devicesCombo.setConverter(new StringConverter<>() {
|
devicesCombo.setConverter(new StringConverter<>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(CaptureDevice device) {
|
public String toString(CaptureDevice device) {
|
||||||
|
|
@ -719,9 +729,14 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
devicesCombo.valueProperty().bindBidirectional(webcamDeviceProperty);
|
devicesCombo.valueProperty().bindBidirectional(webcamDeviceProperty);
|
||||||
ButtonBar.setButtonData(devicesCombo, ButtonBar.ButtonData.LEFT);
|
final ButtonBar.ButtonData buttonData = buttonType.getButtonData();
|
||||||
|
ButtonBar.setButtonData(devicesCombo, buttonData);
|
||||||
button = devicesCombo;
|
button = devicesCombo;
|
||||||
|
} else if(buttonType.getButtonData() == ButtonBar.ButtonData.HELP_2) {
|
||||||
|
ComboBox<WebcamResolution> resolutionsCombo = new ComboBox<>(availableResolutions);
|
||||||
|
resolutionsCombo.valueProperty().bindBidirectional(webcamResolutionProperty);
|
||||||
|
ButtonBar.setButtonData(resolutionsCombo, ButtonBar.ButtonData.LEFT);
|
||||||
|
button = resolutionsCombo;
|
||||||
} else {
|
} else {
|
||||||
button = super.createButton(buttonType);
|
button = super.createButton(buttonType);
|
||||||
}
|
}
|
||||||
|
|
@ -734,19 +749,39 @@ public class QRScanDialog extends Dialog<QRScanDialog.Result> {
|
||||||
button.disableProperty().bind(webcamService.openingProperty());
|
button.disableProperty().bind(webcamService.openingProperty());
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setHdGraphic(ToggleButton hd, boolean isHd) {
|
public static <T extends Comparable<T>> void updateList(List<T> targetList, Collection<T> sourceList) {
|
||||||
if(isHd) {
|
List<T> sortedSource = new ArrayList<>(sourceList);
|
||||||
hd.setGraphic(getGlyph(FontAwesome5.Glyph.CHECK_CIRCLE));
|
Collections.sort(sortedSource);
|
||||||
|
|
||||||
|
ListIterator<T> targetIter = targetList.listIterator();
|
||||||
|
int sourceIndex = 0;
|
||||||
|
|
||||||
|
while (sourceIndex < sortedSource.size() && targetIter.hasNext()) {
|
||||||
|
T sourceItem = sortedSource.get(sourceIndex);
|
||||||
|
T targetItem = targetIter.next();
|
||||||
|
int comparison = sourceItem.compareTo(targetItem);
|
||||||
|
|
||||||
|
if (comparison < 0) {
|
||||||
|
targetIter.previous(); // Back up to insert before
|
||||||
|
targetIter.add(sourceItem);
|
||||||
|
sourceIndex++;
|
||||||
|
} else if (comparison > 0) {
|
||||||
|
targetIter.remove();
|
||||||
} else {
|
} else {
|
||||||
hd.setGraphic(getGlyph(FontAwesome5.Glyph.BAN));
|
sourceIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Glyph getGlyph(FontAwesome5.Glyph glyphName) {
|
while (sourceIndex < sortedSource.size()) {
|
||||||
Glyph glyph = new Glyph(FontAwesome5.FONT_NAME, glyphName);
|
targetIter.add(sortedSource.get(sourceIndex));
|
||||||
glyph.setFontSize(11);
|
sourceIndex++;
|
||||||
return glyph;
|
}
|
||||||
|
|
||||||
|
while (targetIter.hasNext()) {
|
||||||
|
targetIter.next();
|
||||||
|
targetIter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,20 @@ package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import org.openpnp.capture.CaptureFormat;
|
import org.openpnp.capture.CaptureFormat;
|
||||||
|
|
||||||
public enum WebcamResolution {
|
import java.util.Arrays;
|
||||||
VGA(640, 480),
|
|
||||||
HD(1280, 720);
|
|
||||||
|
|
||||||
|
public enum WebcamResolution implements Comparable<WebcamResolution> {
|
||||||
|
VGA("480p", 640, 480),
|
||||||
|
HD("720p", 1280, 720),
|
||||||
|
FHD("1080p", 1920, 1080),
|
||||||
|
UHD4K("4K", 3840, 2160);
|
||||||
|
|
||||||
|
private final String name;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
|
|
||||||
WebcamResolution(int width, int height) {
|
WebcamResolution(String name, int width, int height) {
|
||||||
|
this.name = name;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +24,14 @@ public enum WebcamResolution {
|
||||||
return this.width * this.height;
|
return this.width * this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStandardAspect() {
|
||||||
|
return Arrays.equals(getAspectRatio(), new int[]{4, 3});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWidescreenAspect() {
|
||||||
|
return Arrays.equals(getAspectRatio(), new int[]{16, 9});
|
||||||
|
}
|
||||||
|
|
||||||
public int[] getAspectRatio() {
|
public int[] getAspectRatio() {
|
||||||
int factor = this.getCommonFactor(this.width, this.height);
|
int factor = this.getCommonFactor(this.width, this.height);
|
||||||
int wr = this.width / factor;
|
int wr = this.width / factor;
|
||||||
|
|
@ -29,6 +43,10 @@ public enum WebcamResolution {
|
||||||
return height == 0 ? width : this.getCommonFactor(height, width % height);
|
return height == 0 ? width : this.getCommonFactor(height, width % height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return this.width;
|
return this.width;
|
||||||
}
|
}
|
||||||
|
|
@ -38,8 +56,7 @@ public enum WebcamResolution {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
int[] ratio = this.getAspectRatio();
|
return name;
|
||||||
return super.toString() + ' ' + this.width + 'x' + this.height + " (" + ratio[0] + ':' + ratio[1] + ')';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebcamResolution from(CaptureFormat captureFormat) {
|
public static WebcamResolution from(CaptureFormat captureFormat) {
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ 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.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
|
@ -29,15 +27,18 @@ import java.awt.*;
|
||||||
import java.awt.geom.RoundRectangle2D;
|
import java.awt.geom.RoundRectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.WritableRaster;
|
import java.awt.image.WritableRaster;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class WebcamService extends ScheduledService<Image> {
|
public class WebcamService extends ScheduledService<Image> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(WebcamService.class);
|
private static final Logger log = LoggerFactory.getLogger(WebcamService.class);
|
||||||
|
|
||||||
|
private List<CaptureDevice> devices;
|
||||||
|
private Set<WebcamResolution> resolutions;
|
||||||
|
|
||||||
private WebcamResolution resolution;
|
private WebcamResolution resolution;
|
||||||
private CaptureDevice device;
|
private CaptureDevice device;
|
||||||
private final BooleanProperty opening = new SimpleBooleanProperty(false);
|
private final BooleanProperty opening = new SimpleBooleanProperty(false);
|
||||||
|
|
@ -50,7 +51,6 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
private final OpenPnpCapture capture;
|
private final OpenPnpCapture capture;
|
||||||
private CaptureStream stream;
|
private CaptureStream stream;
|
||||||
private long lastQrSampleTime;
|
private long lastQrSampleTime;
|
||||||
private final ObservableList<CaptureDevice> foundDevices = FXCollections.observableList(new ArrayList<>());
|
|
||||||
private final Reader qrReader;
|
private final Reader qrReader;
|
||||||
private final Bokmakierie bokmakierie;
|
private final Bokmakierie bokmakierie;
|
||||||
|
|
||||||
|
|
@ -94,27 +94,22 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
protected Image call() throws Exception {
|
protected Image call() throws Exception {
|
||||||
try {
|
try {
|
||||||
if(stream == null) {
|
if(stream == null) {
|
||||||
List<CaptureDevice> devices = capture.getDevices();
|
devices = capture.getDevices();
|
||||||
|
|
||||||
List<CaptureDevice> newDevices = new ArrayList<>(devices);
|
if(devices.isEmpty()) {
|
||||||
newDevices.removeAll(foundDevices);
|
|
||||||
foundDevices.addAll(newDevices);
|
|
||||||
foundDevices.removeIf(device -> !devices.contains(device));
|
|
||||||
|
|
||||||
if(foundDevices.isEmpty()) {
|
|
||||||
throw new UnsupportedOperationException("No cameras available");
|
throw new UnsupportedOperationException("No cameras available");
|
||||||
}
|
}
|
||||||
|
|
||||||
CaptureDevice selectedDevice = foundDevices.getFirst();
|
CaptureDevice selectedDevice = devices.getFirst();
|
||||||
|
|
||||||
if(device != null) {
|
if(device != null) {
|
||||||
for(CaptureDevice webcam : foundDevices) {
|
for(CaptureDevice webcam : devices) {
|
||||||
if(webcam.getName().equals(device.getName())) {
|
if(webcam.getName().equals(device.getName())) {
|
||||||
selectedDevice = webcam;
|
selectedDevice = webcam;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(Config.get().getWebcamDevice() != null) {
|
} else if(Config.get().getWebcamDevice() != null) {
|
||||||
for(CaptureDevice webcam : foundDevices) {
|
for(CaptureDevice webcam : devices) {
|
||||||
if(webcam.getName().equals(Config.get().getWebcamDevice())) {
|
if(webcam.getName().equals(Config.get().getWebcamDevice())) {
|
||||||
selectedDevice = webcam;
|
selectedDevice = webcam;
|
||||||
}
|
}
|
||||||
|
|
@ -129,17 +124,22 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
|
|
||||||
Map<WebcamResolution, CaptureFormat> supportedResolutions = device.getFormats().stream()
|
Map<WebcamResolution, CaptureFormat> supportedResolutions = device.getFormats().stream()
|
||||||
.filter(f -> WebcamResolution.from(f) != null)
|
.filter(f -> WebcamResolution.from(f) != null)
|
||||||
.collect(Collectors.toMap(WebcamResolution::from, Function.identity(), (u, v) -> u));
|
.collect(Collectors.toMap(WebcamResolution::from, Function.identity(), (u, v) -> u, TreeMap::new));
|
||||||
|
resolutions = supportedResolutions.keySet();
|
||||||
|
|
||||||
CaptureFormat format = supportedResolutions.get(resolution);
|
CaptureFormat format = supportedResolutions.get(resolution);
|
||||||
if(format == null) {
|
if(format == null) {
|
||||||
if(!supportedResolutions.isEmpty()) {
|
if(!supportedResolutions.isEmpty()) {
|
||||||
format = supportedResolutions.values().iterator().next();
|
resolution = getNearestEnum(resolution, supportedResolutions.keySet().toArray(new WebcamResolution[0]));
|
||||||
|
format = supportedResolutions.get(resolution);
|
||||||
} else {
|
} else {
|
||||||
format = device.getFormats().getFirst();
|
format = device.getFormats().getFirst();
|
||||||
|
log.warn("Could not get standard capture resolution, using " + format.getFormatInfo().width + "x" + format.getFormatInfo().height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn("Could not get requested capture resolution, using " + format.getFormatInfo().width + "x" + format.getFormatInfo().height);
|
if(log.isDebugEnabled()) {
|
||||||
|
log.debug("Opening capture stream with format " + format.getFormatInfo().width + "x" + format.getFormatInfo().height + " (" + fourCCToString(format.getFormatInfo().fourcc) + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
opening.set(true);
|
opening.set(true);
|
||||||
|
|
@ -237,7 +237,7 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
g2d.drawImage(image, 0, 0, null);
|
g2d.drawImage(image, 0, 0, null);
|
||||||
float[] dash1 = {10.0f};
|
float[] dash1 = {10.0f};
|
||||||
g2d.setColor(Color.BLACK);
|
g2d.setColor(Color.BLACK);
|
||||||
g2d.setStroke(new BasicStroke(resolution == WebcamResolution.HD ? 3.0f : 1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f));
|
g2d.setStroke(new BasicStroke(resolution.isWidescreenAspect() ? 3.0f : 1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f));
|
||||||
g2d.draw(new RoundRectangle2D.Double(cropped.x, cropped.y, cropped.length, cropped.length, 10, 10));
|
g2d.draw(new RoundRectangle2D.Double(cropped.x, cropped.y, cropped.length, cropped.length, 10, 10));
|
||||||
g2d.dispose();
|
g2d.dispose();
|
||||||
return clone;
|
return clone;
|
||||||
|
|
@ -274,6 +274,14 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<CaptureDevice> getDevices() {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<WebcamResolution> getResolutions() {
|
||||||
|
return resolutions;
|
||||||
|
}
|
||||||
|
|
||||||
public Result getResult() {
|
public Result getResult() {
|
||||||
return resultProperty.get();
|
return resultProperty.get();
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +298,10 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
return resolution.getHeight();
|
return resolution.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WebcamResolution getResolution() {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResolution(WebcamResolution resolution) {
|
public void setResolution(WebcamResolution resolution) {
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
}
|
}
|
||||||
|
|
@ -302,10 +314,6 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableList<CaptureDevice> getFoundDevices() {
|
|
||||||
return foundDevices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanProperty openingProperty() {
|
public BooleanProperty openingProperty() {
|
||||||
return opening;
|
return opening;
|
||||||
}
|
}
|
||||||
|
|
@ -323,6 +331,17 @@ public class WebcamService extends ScheduledService<Image> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> T getNearestEnum(T target) {
|
||||||
|
return getNearestEnum(target, target.getDeclaringClass().getEnumConstants());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> T getNearestEnum(T target, T[] values) {
|
||||||
|
int ordinal = target.ordinal();
|
||||||
|
return Stream.concat(ordinal > 0 ? Stream.of(values[ordinal - 1]) : Stream.empty(), ordinal < values.length - 1 ? Stream.of(values[ordinal + 1]) : Stream.empty())
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
private static class CroppedDimension {
|
private static class CroppedDimension {
|
||||||
public int x;
|
public int x;
|
||||||
public int y;
|
public int y;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
package com.sparrowwallet.sparrow.event;
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
import com.sparrowwallet.sparrow.control.WebcamResolution;
|
||||||
|
|
||||||
public class WebcamResolutionChangedEvent {
|
public class WebcamResolutionChangedEvent {
|
||||||
private final boolean hdResolution;
|
private final WebcamResolution resolution;
|
||||||
|
|
||||||
public WebcamResolutionChangedEvent(boolean hdResolution) {
|
public WebcamResolutionChangedEvent(WebcamResolution resolution) {
|
||||||
this.hdResolution = hdResolution;
|
this.resolution = resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHdResolution() {
|
public WebcamResolution getResolution() {
|
||||||
return hdResolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.sparrowwallet.sparrow.UnitFormat;
|
||||||
import com.sparrowwallet.sparrow.Mode;
|
import com.sparrowwallet.sparrow.Mode;
|
||||||
import com.sparrowwallet.sparrow.Theme;
|
import com.sparrowwallet.sparrow.Theme;
|
||||||
import com.sparrowwallet.sparrow.control.QRDensity;
|
import com.sparrowwallet.sparrow.control.QRDensity;
|
||||||
|
import com.sparrowwallet.sparrow.control.WebcamResolution;
|
||||||
import com.sparrowwallet.sparrow.net.*;
|
import com.sparrowwallet.sparrow.net.*;
|
||||||
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
|
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
|
||||||
import com.sparrowwallet.sparrow.wallet.OptimizationStrategy;
|
import com.sparrowwallet.sparrow.wallet.OptimizationStrategy;
|
||||||
|
|
@ -56,7 +57,7 @@ public class Config {
|
||||||
private long dustAttackThreshold = DUST_ATTACK_THRESHOLD_SATS;
|
private long dustAttackThreshold = DUST_ATTACK_THRESHOLD_SATS;
|
||||||
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
|
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
|
||||||
private QRDensity qrDensity;
|
private QRDensity qrDensity;
|
||||||
private Boolean hdCapture;
|
private WebcamResolution webcamResolution;
|
||||||
private boolean mirrorCapture = true;
|
private boolean mirrorCapture = true;
|
||||||
private boolean useZbar = true;
|
private boolean useZbar = true;
|
||||||
private String webcamDevice;
|
private String webcamDevice;
|
||||||
|
|
@ -383,16 +384,12 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getHdCapture() {
|
public WebcamResolution getWebcamResolution() {
|
||||||
return hdCapture;
|
return webcamResolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean isHdCapture() {
|
public void setWebcamResolution(WebcamResolution webcamResolution) {
|
||||||
return hdCapture != null && hdCapture;
|
this.webcamResolution = webcamResolution;
|
||||||
}
|
|
||||||
|
|
||||||
public void setHdCapture(Boolean hdCapture) {
|
|
||||||
this.hdCapture = hdCapture;
|
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue