From ba33d871d3dfac8f6c8deda0920ae0c088101f41 Mon Sep 17 00:00:00 2001 From: Igrium Date: Thu, 21 Jul 2022 18:52:50 -0700 Subject: [PATCH] added hi-res screenshot function to gui{ --- .../blockmap/gui/ScreenshotHandler.java | 70 +++++++++++++++++++ .../blockmap/gui/WorldRendererCanvas.java | 4 ++ .../gui/standalone/GuiController.java | 49 +++++++++++++ .../blockmap/gui/standalone/scene.fxml | 6 ++ 4 files changed, 129 insertions(+) create mode 100644 BlockMap-gui/src/main/java/de/piegames/blockmap/gui/ScreenshotHandler.java diff --git a/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/ScreenshotHandler.java b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/ScreenshotHandler.java new file mode 100644 index 0000000..2fc8216 --- /dev/null +++ b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/ScreenshotHandler.java @@ -0,0 +1,70 @@ +package de.piegames.blockmap.gui; + +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.joml.AABBd; + +import javafx.concurrent.Task; +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.SnapshotParameters; +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.image.Image; +import javafx.scene.image.WritableImage; +import javafx.scene.paint.Color; + +/** + * Handles the creation of high-resolution screenshots of maps. + */ +public class ScreenshotHandler { + + /** + * Take a high-resolution screenshot of a section of them map + * @param map Map to use. + * @param frustum Bounds of the image. + * @return The captured image. + */ + public static WritableImage takeScreenshot(RenderedMap map, AABBd frustum) { + double width = frustum.maxX - frustum.minX; + double height = frustum.maxY - frustum.minY; + + Canvas canvas = new Canvas(width, height); + GraphicsContext gc = canvas.getGraphicsContext2D(); + gc.translate(-frustum.minX, -frustum.minY); + + map.draw(gc, 0, frustum, 1); + + SnapshotParameters parameters = new SnapshotParameters(); + parameters.setFill(Color.TRANSPARENT); + + return canvas.snapshot(parameters, null); + } + + /** + * Save a JavaFX image out to a file in PNG format. + * + * @param image Image to save. + * @param file File to save to. + * @throws IOException If an IO exception occurs while writing the file. + */ + public static void saveImage(Image image, File file) throws IOException { + ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file); + } + + public static Task saveImageTask(Image image, File file) { + Task task = new Task() { + + @Override + protected Void call() throws Exception { + saveImage(image, file); + return null; + } + + }; + return task; + } + +} diff --git a/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/WorldRendererCanvas.java b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/WorldRendererCanvas.java index 9a870e4..68710a7 100644 --- a/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/WorldRendererCanvas.java +++ b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/WorldRendererCanvas.java @@ -113,6 +113,10 @@ public void render() { // gc.strokeRect(0, 0, getWidth() - 0, getHeight() - 0); } + public RenderedMap getMap() { + return map; + } + public ReadOnlyMapProperty> getChunkMetadata() { return chunkMetadata.getReadOnlyProperty(); } diff --git a/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiController.java b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiController.java index e265034..731c54f 100644 --- a/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiController.java +++ b/BlockMap-gui/src/main/java/de/piegames/blockmap/gui/standalone/GuiController.java @@ -1,5 +1,6 @@ package de.piegames.blockmap.gui.standalone; +import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -12,6 +13,7 @@ import java.util.Map; import java.util.ResourceBundle; import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -21,12 +23,14 @@ import org.controlsfx.control.CheckTreeView; import org.controlsfx.control.StatusBar; import org.controlsfx.dialog.ExceptionDialog; +import org.controlsfx.dialog.ProgressDialog; import org.joml.Vector2d; import org.joml.Vector2ic; import com.google.common.util.concurrent.ThreadFactoryBuilder; import de.piegames.blockmap.gui.MapPane; +import de.piegames.blockmap.gui.ScreenshotHandler; import de.piegames.blockmap.gui.WorldRendererCanvas; import de.piegames.blockmap.gui.decoration.DragScrollDecoration; import de.piegames.blockmap.gui.decoration.GridDecoration; @@ -49,6 +53,7 @@ import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.MapChangeListener; +import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Alert; @@ -57,12 +62,17 @@ import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBoxTreeItem; import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; import javafx.scene.control.SelectionMode; import javafx.scene.control.TitledPane; import javafx.scene.control.TreeItem; import javafx.scene.image.ImageView; +import javafx.scene.image.WritableImage; import javafx.scene.input.MouseButton; import javafx.scene.layout.BorderPane; +import javafx.stage.FileChooser; +import javafx.stage.Modality; +import javafx.stage.FileChooser.ExtensionFilter; import javafx.util.Duration; import javafx.util.Pair; @@ -111,6 +121,11 @@ static enum WorldType { public CheckTreeView pinView; public Map> checkedPins = new HashMap<>(); + /* Menu Bar */ + @FXML + protected MenuItem screenshotButton; + + protected MapPane pane; public PinDecoration pins; @@ -265,6 +280,14 @@ public void initialize(URL location, ResourceBundle resources) { if (change.getValueAdded() != null) GuiController.this.pins.loadRegion(change.getKey(), Pin.convertDynamic(change.getValueAdded(), renderer.viewport)); }); + + regionFolder.addListener((e, old, val) -> { + if (val == null) { + screenshotButton.setDisable(true); + } else { + screenshotButton.setDisable(false); + } + }); } /** @@ -367,6 +390,32 @@ public void load(String input) { alert.showAndWait(); } + @FXML + public void screenshot() { + WritableImage image = ScreenshotHandler.takeScreenshot(renderer.getMap(), renderer.viewport.getFrustum()); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save Screenshot"); + fileChooser.getExtensionFilters().add(new ExtensionFilter("PNG Files", "*.png")); + File file = fileChooser.showSaveDialog(null); + if (file != null) { + + Task task = ScreenshotHandler.saveImageTask(image, file); + task.setOnFailed(value -> { + log.error("Unable to save screenshot.", task.getException()); + ExceptionDialog d = new ExceptionDialog(task.getException()); + d.setTitle("Error"); + d.setHeaderText("Unable to save screenshot."); + d.showAndWait(); + }); + + ProgressDialog progress = new ProgressDialog(task); + progress.setTitle("Saving"); + progress.setHeaderText("Saving screenshot"); + progress.initModality(Modality.APPLICATION_MODAL); + ForkJoinPool.commonPool().execute(task::run); + } + } + @FXML public void showLoadDialog() { try { diff --git a/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/standalone/scene.fxml b/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/standalone/scene.fxml index f767846..aa48d8e 100644 --- a/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/standalone/scene.fxml +++ b/BlockMap-gui/src/main/resources/de/piegames/blockmap/gui/standalone/scene.fxml @@ -75,6 +75,12 @@ shortcut="UP" /> + +