diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotInput.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotInput.java index 3d6554e4fa..1400f94639 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotInput.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotInput.java @@ -60,7 +60,7 @@ public class ScreenshotInput implements KeyListener if (event.getKeyCode() == KeyEvent.VK_INSERT) { - plugin.takeScreenshot(TIME_FORMAT.format(new Date()), config.displayDate()); + plugin.takeScreenshot(TIME_FORMAT.format(new Date())); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotOverlay.java new file mode 100644 index 0000000000..d3593f8e48 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotOverlay.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018, Lotto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.screenshot; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Consumer; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.MainBufferProvider; +import net.runelite.client.ui.DrawManager; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; + +@Slf4j +public class ScreenshotOverlay extends Overlay +{ + private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MMM. dd, yyyy", Locale.US); + private static final int REPORT_BUTTON_X_OFFSET = 404; + private static BufferedImage REPORT_BUTTON; + + static + { + try + { + synchronized (ImageIO.class) + { + REPORT_BUTTON = ImageIO.read(ScreenshotPlugin.class.getResourceAsStream("report_button.png")); + } + } + catch (IOException e) + { + log.warn("Report button image failed to load", e); + } + } + + private final Client client; + private final DrawManager drawManager; + + private final Queue> consumers = new ConcurrentLinkedQueue<>(); + + @Inject + public ScreenshotOverlay(Client client, DrawManager drawManager) + { + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.drawManager = drawManager; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (consumers.isEmpty()) + { + return null; + } + + final MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider(); + final int imageHeight = ((BufferedImage) bufferProvider.getImage()).getHeight(); + final int y = imageHeight - REPORT_BUTTON.getHeight() - 1; + + graphics.drawImage(REPORT_BUTTON, REPORT_BUTTON_X_OFFSET, y, null); + + graphics.setFont(FontManager.getRunescapeSmallFont()); + FontMetrics fontMetrics = graphics.getFontMetrics(); + + String date = DATE_FORMAT.format(new Date()); + final int dateWidth = fontMetrics.stringWidth(date); + final int dateHeight = fontMetrics.getHeight(); + + final int textX = REPORT_BUTTON_X_OFFSET + REPORT_BUTTON.getWidth() / 2 - dateWidth / 2; + final int textY = y + REPORT_BUTTON.getHeight() / 2 + dateHeight / 2; + + graphics.setColor(Color.BLACK); + graphics.drawString(date, textX + 1, textY + 1); + + graphics.setColor(Color.WHITE); + graphics.drawString(date, textX, textY); + + // Request the queued screenshots to be taken, + // now that the timestamp is visible. + Consumer consumer; + while ((consumer = consumers.poll()) != null) + { + drawManager.requestNextFrameListener(consumer); + } + + return null; + } + + public void queueForTimestamp(Consumer screenshotConsumer) + { + if (REPORT_BUTTON == null) + { + return; + } + + consumers.add(screenshotConsumer); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index e3b948da7c..a97c134735 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -28,9 +28,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; -import java.awt.Color; import java.awt.Desktop; -import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.TrayIcon; @@ -47,6 +45,7 @@ import java.time.LocalDate; import java.util.Date; import java.util.Locale; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.imageio.ImageIO; @@ -79,9 +78,9 @@ import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.screenshot.imgur.ImageUploadRequest; import net.runelite.client.plugins.screenshot.imgur.ImageUploadResponse; import net.runelite.client.ui.ClientUI; -import net.runelite.client.ui.FontManager; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.TitleToolbar; +import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.util.Text; import net.runelite.http.api.RuneLiteAPI; import okhttp3.Call; @@ -102,7 +101,6 @@ public class ScreenshotPlugin extends Plugin private static final HttpUrl IMGUR_IMAGE_UPLOAD_URL = HttpUrl.parse("https://api.imgur.com/3/image"); private static final MediaType JSON = MediaType.parse("application/json"); - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MMM. dd, yyyy", Locale.US); static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US); private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); @@ -118,6 +116,9 @@ public class ScreenshotPlugin extends Plugin @Inject private ScreenshotConfig config; + @Inject + private ScreenshotOverlay screenshotOverlay; + @Inject private Notifier notifier; @@ -150,6 +151,12 @@ public class ScreenshotPlugin extends Plugin return configManager.getConfig(ScreenshotConfig.class); } + @Override + public Overlay getOverlay() + { + return screenshotOverlay; + } + @Override protected void startUp() throws Exception { @@ -167,9 +174,7 @@ public class ScreenshotPlugin extends Plugin titleBarButton = NavigationButton.builder() .tooltip("Take screenshot") .icon(iconImage) - .onClick(() -> takeScreenshot( - TIME_FORMAT.format(new Date()), - client.getLocalPlayer() != null)) + .onClick(() -> takeScreenshot(TIME_FORMAT.format(new Date()))) .popup(ImmutableMap .builder() .put("Open screenshot folder...", () -> @@ -254,7 +259,7 @@ public class ScreenshotPlugin extends Plugin if (event.getGroupId() == WidgetID.KINGDOM_GROUP_ID) { String fileName = "Kingdom " + LocalDate.now(); - takeScreenshot(fileName, config.displayDate()); + takeScreenshot(fileName); } } @@ -271,6 +276,7 @@ public class ScreenshotPlugin extends Plugin switch (TO_GROUP(widget.getId())) { case LEVEL_UP_GROUP_ID: + case DIALOG_SPRITE_GROUP_ID: if (!config.screenshotLevels()) { return; @@ -359,7 +365,7 @@ public class ScreenshotPlugin extends Plugin return; } - takeScreenshot(fileName, config.displayDate()); + takeScreenshot(fileName); } /** @@ -394,9 +400,8 @@ public class ScreenshotPlugin extends Plugin * and optionally uploads it to an image-hosting service. * * @param fileName Filename to use, without file extension. - * @param displayDate Whether to show today's date on the report button as the screenshot is taken. */ - void takeScreenshot(String fileName, boolean displayDate) + void takeScreenshot(String fileName) { if (client.getGameState() == GameState.LOGIN_SCREEN) { @@ -405,7 +410,7 @@ public class ScreenshotPlugin extends Plugin return; } - drawManager.requestNextFrameListener(image -> + Consumer screenshotConsumer = image -> { BufferedImage screenshot = config.includeFrame() ? new BufferedImage(clientUi.getWidth(), clientUi.getHeight(), BufferedImage.TYPE_INT_ARGB) @@ -430,43 +435,6 @@ public class ScreenshotPlugin extends Plugin // Draw the game onto the screenshot graphics.drawImage(image, gameOffsetX, gameOffsetY, null); - if (displayDate) - { - try (InputStream reportButton = ScreenshotPlugin.class.getResourceAsStream("report_button.png")) - { - BufferedImage reportButtonImage; - synchronized (ImageIO.class) - { - reportButtonImage = ImageIO.read(reportButton); - } - - int x = gameOffsetX + 403; - int y = gameOffsetY + image.getHeight() - reportButtonImage.getHeight() - 1; - - graphics.drawImage(reportButtonImage, x, y, null); - - graphics.setFont(FontManager.getRunescapeSmallFont()); - FontMetrics fontMetrics = graphics.getFontMetrics(); - - String date = DATE_FORMAT.format(new Date()); - int dateWidth = fontMetrics.stringWidth(date); - int dateHeight = fontMetrics.getHeight(); - - int textX = x + reportButtonImage.getWidth() / 2 - dateWidth / 2; - int textY = y + reportButtonImage.getHeight() / 2 + dateHeight / 2; - - graphics.setColor(Color.BLACK); - graphics.drawString(date, textX + 1, textY + 1); - - graphics.setColor(Color.WHITE); - graphics.drawString(date, textX, textY); - } - catch (Exception ex) - { - log.warn("error displaying date on screenshot", ex); - } - } - File playerFolder; if (client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null) { @@ -501,7 +469,16 @@ public class ScreenshotPlugin extends Plugin log.warn("error writing screenshot", ex); } }); - }); + }; + + if (config.displayDate()) + { + screenshotOverlay.queueForTimestamp(screenshotConsumer); + } + else + { + drawManager.requestNextFrameListener(screenshotConsumer); + } } /**