package games.strategy.triplea.ui.export; import static com.google.common.base.Preconditions.checkNotNull; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Optional; import javax.imageio.ImageIO; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import games.strategy.engine.data.GameData; import games.strategy.engine.history.HistoryNode; import games.strategy.engine.history.Round; import games.strategy.triplea.ui.IUIContext; import games.strategy.triplea.ui.MapPanel; import games.strategy.triplea.ui.TripleAFrame; import games.strategy.triplea.ui.mapdata.MapData; import games.strategy.ui.SwingComponents; import games.strategy.ui.Util; public final class ScreenshotExporter { private final TripleAFrame frame; private ScreenshotExporter(final TripleAFrame frame) { this.frame = frame; } /** * Prompts the user for the file to which the screenshot will be saved and saves the screenshot for the specified game * at the specified history step to that file. * * @param frame The frame associated with the game screenshot to export; must not be {@code null}. * @param gameData The game data; must not be {@code null}. * @param node The history step at which the game screenshot is to be captured; must not be {@code null}. */ public static void exportScreenshot(final TripleAFrame frame, final GameData gameData, final HistoryNode node) { checkNotNull(frame); checkNotNull(gameData); checkNotNull(node); final ScreenshotExporter exporter = new ScreenshotExporter(frame); exporter.promptSaveFile().ifPresent(file -> exporter.runSave(gameData, node, file)); } private Optional<File> promptSaveFile() { return SwingComponents.promptSaveFile(frame, "png", "Saved Map Snapshots"); } private void runSave(final GameData gameData, final HistoryNode node, final File file) { SwingComponents.runWithProgressBar(frame, "Saving map snapshot...", () -> { save(gameData, node, file); return null; }).whenComplete((ignore, e) -> { SwingUtilities.invokeLater(() -> { if (e == null) { JOptionPane.showMessageDialog(frame, "Map Snapshot Saved", "Map Snapshot Saved", JOptionPane.INFORMATION_MESSAGE); } else { JOptionPane.showMessageDialog(frame, e.getMessage(), "Error Saving Map Snapshot", JOptionPane.ERROR_MESSAGE); } }); }); } private void save(final GameData gameData, final HistoryNode node, final File file) throws IOException { // get round/step/player from history tree int round = 0; final Object[] pathFromRoot = node.getPath(); for (final Object pathNode : pathFromRoot) { final HistoryNode curNode = (HistoryNode) pathNode; if (curNode instanceof Round) { round = ((Round) curNode).getRoundNo(); } } final IUIContext iuiContext = frame.getUIContext(); final double scale = iuiContext.getScale(); // print map panel to image final MapPanel mapPanel = frame.getMapPanel(); final BufferedImage mapImage = Util.createImage((int) (scale * mapPanel.getImageWidth()), (int) (scale * mapPanel.getImageHeight()), false); final Graphics2D mapGraphics = mapImage.createGraphics(); try { // workaround to get the whole map // (otherwise the map is cut if current window is not on top of map) final int xOffset = mapPanel.getXOffset(); final int yOffset = mapPanel.getYOffset(); mapPanel.setTopLeft(0, 0); mapPanel.drawMapImage(mapGraphics); mapPanel.setTopLeft(xOffset, yOffset); // overlay title Color title_color = iuiContext.getMapData().getColorProperty(MapData.PROPERTY_SCREENSHOT_TITLE_COLOR); if (title_color == null) { title_color = Color.BLACK; } final String s_title_x = iuiContext.getMapData().getProperty(MapData.PROPERTY_SCREENSHOT_TITLE_X); final String s_title_y = iuiContext.getMapData().getProperty(MapData.PROPERTY_SCREENSHOT_TITLE_Y); final String s_title_size = iuiContext.getMapData().getProperty(MapData.PROPERTY_SCREENSHOT_TITLE_FONT_SIZE); int title_x; int title_y; int title_size; try { title_x = (int) (Integer.parseInt(s_title_x) * scale); title_y = (int) (Integer.parseInt(s_title_y) * scale); title_size = Integer.parseInt(s_title_size); } catch (final NumberFormatException nfe) { // choose safe defaults title_x = (int) (15 * scale); title_y = (int) (15 * scale); title_size = 15; } // everything else should be scaled down onto map image final AffineTransform transform = new AffineTransform(); transform.scale(scale, scale); mapGraphics.setTransform(transform); mapGraphics.setFont(new Font("Ariel", Font.BOLD, title_size)); mapGraphics.setColor(title_color); if (iuiContext.getMapData().getBooleanProperty(MapData.PROPERTY_SCREENSHOT_TITLE_ENABLED)) { mapGraphics.drawString(gameData.getGameName() + " Round " + round, title_x, title_y); } // save Image as .png ImageIO.write(mapImage, "png", file); } finally { // Clean up objects. There might be some overkill here, // but there were memory leaks that are fixed by some/all of these. mapImage.flush(); mapGraphics.dispose(); } } }