From 8fb36d3ea4ece0135a7217a93ef760a475d695dc Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Sun, 29 Jul 2018 19:14:14 -0700 Subject: [PATCH] resources: Remove derivative images This commit adds `ImageUtil::bufferedImageFromImage`, `ImageUtil::alphaOffset`, `ImageUtil::grayscaleImage`, and `ImageUtil::flipImage` functions and applies them to client and plugin code to remove unneeded derivative images from the resources directory. --- .../client/plugins/config/ConfigPanel.java | 2 +- .../client/plugins/config/PluginListItem.java | 18 ++- .../grandexchange/GrandExchangeOfferSlot.java | 5 +- .../GrandExchangeSearchPanel.java | 3 +- .../client/plugins/info/InfoPanel.java | 2 +- .../kourendlibrary/KourendLibraryPanel.java | 2 +- .../ui/ScreenMarkerCreationPanel.java | 10 +- .../screenmarkers/ui/ScreenMarkerPanel.java | 18 +-- .../ui/ScreenMarkerPluginPanel.java | 4 +- .../java/net/runelite/client/ui/ClientUI.java | 5 +- .../net/runelite/client/util/ImageUtil.java | 146 +++++++++++++++++- .../config/{stars/on.png => star_on.png} | Bin .../client/plugins/config/stars/off.png | Bin 654 -> 0 bytes .../{switchers/on.png => switcher_on.png} | Bin .../client/plugins/config/switchers/off.png | Bin 15659 -> 0 bytes .../plugins/grandexchange/arrow_left.png | Bin 15918 -> 0 bytes .../plugins/grandexchange/arrow_right.png | Bin 15917 -> 0 bytes .../plugins/screenmarkers/add_hover_icon.png | Bin 15845 -> 0 bytes .../screenmarkers/cancel_hover_icon.png | Bin 16152 -> 0 bytes .../screenmarkers/confirm_hover_icon.png | Bin 15951 -> 0 bytes .../screenmarkers/confirm_locked_icon.png | Bin 15950 -> 0 bytes .../net/runelite/client/ui/close.png | Bin 242 -> 0 bytes .../client/ui/components/search_darker.png | Bin 16280 -> 0 bytes .../plugins/info => util}/arrow_right.png | Bin .../runelite/client/util/ImageUtilTest.java | 111 +++++++++++++ 25 files changed, 292 insertions(+), 34 deletions(-) rename runelite-client/src/main/resources/net/runelite/client/plugins/config/{stars/on.png => star_on.png} (100%) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/stars/off.png rename runelite-client/src/main/resources/net/runelite/client/plugins/config/{switchers/on.png => switcher_on.png} (100%) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/cancel_hover_icon.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_locked_icon.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/close.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/ui/components/search_darker.png rename runelite-client/src/main/resources/{net/runelite/client/plugins/info => util}/arrow_right.png (100%) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index e5636314f9..49dc527f3d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -130,7 +130,7 @@ public class ConfigPanel extends PluginPanel { BufferedImage backIcon = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_back_icon.png")); BACK_ICON = new ImageIcon(backIcon); - BACK_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(backIcon, -100)); + BACK_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backIcon, -100)); SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png"))); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 27b96d2c27..b9ba675b80 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -92,13 +92,21 @@ class PluginListItem extends JPanel { configIcon = ImageIO.read(ConfigPanel.class.getResourceAsStream("config_edit_icon.png")); CONFIG_ICON = new ImageIcon(configIcon); - ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/on.png"))); - OFF_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switchers/off.png"))); - ON_STAR = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("stars/on.png"))); - OFF_STAR = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("stars/off.png"))); + ON_SWITCHER = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("switcher_on.png"))); + ON_STAR = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("star_on.png"))); } - CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(configIcon, -100)); + BufferedImage offSwitcherImage = ImageUtil.bufferedImageFromImage(ON_SWITCHER.getImage()); + offSwitcherImage = ImageUtil.grayscaleImage(offSwitcherImage); + offSwitcherImage = ImageUtil.grayscaleOffset(offSwitcherImage, 0.61f); + offSwitcherImage = ImageUtil.flipImage(offSwitcherImage, true, false); + OFF_SWITCHER = new ImageIcon(offSwitcherImage); + BufferedImage offStarImage = ImageUtil.bufferedImageFromImage(ON_STAR.getImage()); + offStarImage = ImageUtil.grayscaleImage(offStarImage); + offStarImage = ImageUtil.grayscaleOffset(offStarImage, 0.77f); + OFF_STAR = new ImageIcon(offStarImage); + + CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(configIcon, -100)); } catch (IOException e) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java index 356d6f843c..45fdbe5371 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeOfferSlot.java @@ -54,6 +54,7 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.ThinProgressBar; import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.StackFormatter; @Slf4j @@ -87,8 +88,8 @@ public class GrandExchangeOfferSlot extends JPanel { synchronized (ImageIO.class) { - RIGHT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_right.png"))); - LEFT_ARROW_ICON = new ImageIcon(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("arrow_left.png"))); + RIGHT_ARROW_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageIO.read(GrandExchangeOfferSlot.class.getResourceAsStream("/util/arrow_right.png")), 0.25f)); + LEFT_ARROW_ICON = new ImageIcon(ImageUtil.flipImage(ImageUtil.bufferedImageFromImage(RIGHT_ARROW_ICON.getImage()), true, false)); } } catch (IOException e) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java index 05e92315c4..2cf3eca92d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java @@ -49,6 +49,7 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.components.IconTextField; import net.runelite.client.ui.components.PluginErrorPanel; +import net.runelite.client.util.ImageUtil; import net.runelite.http.api.item.Item; import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.item.SearchResult; @@ -99,7 +100,7 @@ class GrandExchangeSearchPanel extends JPanel { synchronized (ImageIO.class) { - SEARCH_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search_darker.png"))); + SEARCH_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.grayscaleOffset(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")), 0f), 1.75f)); LOADING_ICON = new ImageIcon(IconTextField.class.getResource("loading_spinner.gif")); ERROR_ICON = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("error.png"))); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java index 7415591e34..6309d289d0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/info/InfoPanel.java @@ -96,7 +96,7 @@ public class InfoPanel extends PluginPanel { synchronized (ImageIO.class) { - ARROW_RIGHT_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("arrow_right.png"))); + ARROW_RIGHT_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("/util/arrow_right.png"))); GITHUB_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("github_icon.png"))); DISCORD_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("discord_icon.png"))); PATREON_ICON = new ImageIcon(ImageIO.read(InfoPanel.class.getResourceAsStream("patreon_icon.png"))); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java index cd66a57229..3dcf7e6542 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java @@ -73,7 +73,7 @@ class KourendLibraryPanel extends PluginPanel { BufferedImage resetIcon = ImageIO.read(KourendLibraryPanel.class.getResourceAsStream("reset.png")); RESET_ICON = new ImageIcon(resetIcon); - RESET_CLICK_ICON = new ImageIcon(ImageUtil.grayscaleOffset(resetIcon, -100)); + RESET_CLICK_ICON = new ImageIcon(ImageUtil.alphaOffset(resetIcon, -100)); } } catch (IOException e) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java index 6385fa3163..3cc7162752 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerCreationPanel.java @@ -29,6 +29,7 @@ import java.awt.Color; import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; @@ -39,6 +40,7 @@ import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.util.ImageUtil; public class ScreenMarkerCreationPanel extends JPanel { @@ -59,16 +61,18 @@ public class ScreenMarkerCreationPanel extends JPanel synchronized (ImageIO.class) { CONFIRM_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_icon.png"))); - CONFIRM_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_hover_icon.png"))); - CONFIRM_LOCKED_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("confirm_locked_icon.png"))); CANCEL_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("cancel_icon.png"))); - CANCEL_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("cancel_hover_icon.png"))); } } catch (IOException e) { throw new RuntimeException(e); } + + final BufferedImage confirmIcon = ImageUtil.bufferedImageFromImage(CONFIRM_ICON.getImage()); + CONFIRM_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(confirmIcon, 0.54f)); + CONFIRM_LOCKED_ICON = new ImageIcon(ImageUtil.grayscaleImage(confirmIcon)); + CANCEL_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(CANCEL_ICON.getImage()), 0.6f)); } ScreenMarkerCreationPanel(ScreenMarkerPlugin plugin) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java index def79500dd..0741875188 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java @@ -114,36 +114,36 @@ class ScreenMarkerPanel extends JPanel { BufferedImage borderImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("border_color_icon.png")); BORDER_COLOR_ICON = new ImageIcon(borderImg); - BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(borderImg, -100)); + BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(borderImg, -100)); BufferedImage noBorderImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("no_border_color_icon.png")); NO_BORDER_COLOR_ICON = new ImageIcon(noBorderImg); - NO_BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(noBorderImg, -100)); + NO_BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(noBorderImg, -100)); BufferedImage fillImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("fill_color_icon.png")); FILL_COLOR_ICON = new ImageIcon(fillImg); - FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(fillImg, -100)); + FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(fillImg, -100)); BufferedImage noFillImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("no_fill_color_icon.png")); NO_FILL_COLOR_ICON = new ImageIcon(noFillImg); - NO_FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(noFillImg, -100)); + NO_FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(noFillImg, -100)); BufferedImage opacityImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("opacity_icon.png")); FULL_OPACITY_ICON = new ImageIcon(opacityImg); - OPACITY_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(opacityImg, -100)); - NO_OPACITY_ICON = new ImageIcon(ImageUtil.grayscaleOffset(opacityImg, -150)); + OPACITY_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(opacityImg, -100)); + NO_OPACITY_ICON = new ImageIcon(ImageUtil.alphaOffset(opacityImg, -150)); BufferedImage visibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("visible_icon.png")); VISIBLE_ICON = new ImageIcon(visibleImg); - VISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(visibleImg, -100)); + VISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(visibleImg, -100)); BufferedImage invisibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("invisible_icon.png")); INVISIBLE_ICON = new ImageIcon(invisibleImg); - INVISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(invisibleImg, -100)); + INVISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(invisibleImg, -100)); BufferedImage deleteImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("delete_icon.png")); DELETE_ICON = new ImageIcon(deleteImg); - DELETE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(deleteImg, -100)); + DELETE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(deleteImg, -100)); } } catch (IOException e) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java index 9dca4e8d11..2b58291b28 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPluginPanel.java @@ -47,6 +47,7 @@ import net.runelite.client.plugins.screenmarkers.ScreenMarkerPlugin; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.PluginErrorPanel; +import net.runelite.client.util.ImageUtil; @Singleton public class ScreenMarkerPluginPanel extends PluginPanel @@ -85,13 +86,14 @@ public class ScreenMarkerPluginPanel extends PluginPanel synchronized (ImageIO.class) { ADD_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("add_icon.png"))); - ADD_HOVER_ICON = new ImageIcon(ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("add_hover_icon.png"))); } } catch (IOException e) { throw new RuntimeException(e); } + + ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(ADD_ICON.getImage()), 0.53f)); } public void init() diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index 0539517a65..15882be372 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -70,6 +70,7 @@ import net.runelite.client.events.NavigationButtonAdded; import net.runelite.client.events.NavigationButtonRemoved; import net.runelite.client.input.KeyManager; import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.OSType; import net.runelite.client.util.OSXUtil; import net.runelite.client.util.SwingUtil; @@ -97,7 +98,6 @@ public class ClientUI { BufferedImage icon; BufferedImage sidebarOpen; - BufferedImage sidebarClose; try { @@ -105,7 +105,6 @@ public class ClientUI { icon = ImageIO.read(ClientUI.class.getResourceAsStream("/runelite.png")); sidebarOpen = ImageIO.read(ClientUI.class.getResourceAsStream("open.png")); - sidebarClose = ImageIO.read(ClientUI.class.getResourceAsStream("close.png")); } } catch (IOException e) @@ -115,7 +114,7 @@ public class ClientUI ICON = icon; SIDEBAR_OPEN = sidebarOpen; - SIDEBAR_CLOSE = sidebarClose; + SIDEBAR_CLOSE = ImageUtil.flipImage(SIDEBAR_OPEN, true, false); } @Getter diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index c2aa18f999..13828dad52 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -31,12 +31,33 @@ import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; import java.util.Arrays; +import javax.swing.GrayFilter; /** * Various Image/BufferedImage utilities. */ public class ImageUtil { + /** + * Creates a {@link BufferedImage} from an {@link Image}. + * + * @param image An Image to be converted to a BufferedImage. + * @return A BufferedImage instance of the same given image. + */ + public static BufferedImage bufferedImageFromImage(final Image image) + { + if (image instanceof BufferedImage) + { + return (BufferedImage) image; + } + + final BufferedImage out = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + final Graphics2D g2d = out.createGraphics(); + g2d.drawImage(image, 0, 0, null); + g2d.dispose(); + return out; + } + /** * Offsets an image in the grayscale (darkens/brightens) by a given offset. * @@ -63,6 +84,86 @@ public class ImageUtil return offset(image, scales, offsets); } + /** + * Offsets an image in the grayscale (darkens/brightens) by a given percentage. + * + * @param image The image to be darkened or brightened. + * @param percentage The ratio to darken or brighten the given image. + * Values above 1 will brighten, and values below 1 will darken. + * @return The given image with its brightness scaled by the given percentage. + */ + public static BufferedImage grayscaleOffset(final BufferedImage image, final float percentage) + { + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(offsets, 0f); + for (int i = 0; i < numComponents; i++) + { + scales[i] = percentage; + } + // Set alpha to not scale + scales[numComponents - 1] = 1f; + + return offset(image, scales, offsets); + } + + /** + * Offsets an image's alpha component by a given offset. + * + * @param image The image to be made more or less transparent. + * @param offset A signed 8-bit integer value to modify the image's alpha component with. + * Values above 0 will increase transparency, and values below 0 will decrease + * transparency. + * @return The given image with its alpha component adjusted by the given offset. + */ + public static BufferedImage alphaOffset(final BufferedImage image, final int offset) + { + final float offsetFloat = (float) offset; + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(scales, 1f); + Arrays.fill(offsets, 0f); + offsets[numComponents - 1] = offsetFloat; + return offset(image, scales, offsets); + } + + /** + * Offsets an image's alpha component by a given percentage. + * + * @param image The image to be made more or less transparent. + * @param percentage The ratio to modify the image's alpha component with. + * Values above 1 will increase transparency, and values below 1 will decrease + * transparency. + * @return The given image with its alpha component scaled by the given percentage. + */ + public static BufferedImage alphaOffset(final BufferedImage image, final float percentage) + { + final int numComponents = image.getColorModel().getNumComponents(); + final float[] scales = new float[numComponents]; + final float[] offsets = new float[numComponents]; + + Arrays.fill(scales, 1f); + Arrays.fill(offsets, 0f); + scales[numComponents - 1] = percentage; + return offset(image, scales, offsets); + } + + /** + * Creates a grayscale image from the given image. + * + * @param image The source image to be converted. + * @return A copy of the given imnage, with colors converted to grayscale. + */ + public static BufferedImage grayscaleImage(final BufferedImage image) + { + final Image grayImage = GrayFilter.createDisabledImage(image); + return ImageUtil.bufferedImageFromImage(grayImage); + } + /** * Re-size a BufferedImage to the given dimensions. * @@ -73,13 +174,8 @@ public class ImageUtil */ public static BufferedImage resizeImage(final BufferedImage image, final int newWidth, final int newHeight) { - final Image tmp = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); - final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); - - final Graphics2D g2d = dimg.createGraphics(); - g2d.drawImage(tmp, 0, 0, null); - g2d.dispose(); - return dimg; + final Image resized = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); + return ImageUtil.bufferedImageFromImage(resized); } /** @@ -97,6 +193,42 @@ public class ImageUtil return transformOp.filter(image, null); } + /** + * Flips an image horizontally and/or vertically. + * + * @param image The image to be flipped. + * @param horizontal Whether the image should be flipped horizontally. + * @param vertical Whether the image should be flipped vertically. + * @return The given image, flipped horizontally and/or vertically. + */ + public static BufferedImage flipImage(final BufferedImage image, final boolean horizontal, final boolean vertical) + { + int x = 0; + int y = 0; + int w = image.getWidth(); + int h = image.getHeight(); + + final BufferedImage out = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + final Graphics2D g2d = out.createGraphics(); + + if (horizontal) + { + x = w; + w *= -1; + } + + if (vertical) + { + y = h; + h *= -1; + } + + g2d.drawImage(image, x, y, w, h, null); + g2d.dispose(); + + return out; + } + /** * Performs a rescale operation on the image's color components. * diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/stars/on.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/star_on.png similarity index 100% rename from runelite-client/src/main/resources/net/runelite/client/plugins/config/stars/on.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/config/star_on.png diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/stars/off.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/stars/off.png deleted file mode 100644 index b030162e0c70a0cd7e4efb1de6d6f58700c9f20f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@TGqFnVW(M3hAM`dB6B=jtV<?;Zqle1Gx6p~WYGxKbf-tXS8q>!0ns}yePYv5bpoSKp8QB{;0T;&&% zT$P<{nWAKG$7NGt1vDTxwIorYA~z?m*s8)-32d$vkPQ;nS5g2gDap1~as*kZ5aAo3 z;GAESs$i;TrkiYNVx(Yhsb^?rU}!BPbD z|2&z5_rS3G=jq}YV$pl{@_s#6M}gxX`9G?QEGl&Mb^62E>7nvSRArvS(g&<_oE9or z+B8JfHoG{z=M<5;c)?3WidD30v-!XNE1U{~P)i|M;+e0i#c%@a>8F8gN_ zhu)7ak2gPvcfQM7cG`MZ2#*PCXD*NK7HcP-UHm7Hvn??9d9aO(MLyzSEbkM=D|?P4 zUEOfMFphcGq2NfL*#Akq8Ed_K8ka5p5m`8AhiAQT0JpcIUC^GDk#8Obe|#S{yJ1>k nujQM^+Aq|*?633fJN=ejuueHNA@;Q>C>eRW`njxgN@xNAjIQoC diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/on.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/switcher_on.png similarity index 100% rename from runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/on.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/config/switcher_on.png diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/switchers/off.png deleted file mode 100644 index ff25cc611a7c1dc7e0c79ae35183214f93ba534a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15659 zcmeI3e{dA#8OJvQg3(|E9h6Z#52}H1x4U<_+}(0H@>3J#AY4*Buw~fWeJ@#aw|m^} zkxNKv5YbqbS{hr&7DXT^N|>aIE!CD-Gf`k#v<5>pRfIt-lsXQjX!()8dw01XZ<2R1 zQ~OWny_vh)=Y8Jq^X~h3pZD4KpM9))@jW@&GqWj*%BiX>cfbq{ZUVDgN{{XGYk z2CFY!HB`L%^s(GKHf)&k+}{S@J{fxJrK6D-`F1|H;fT!r`FxB0V()b|)O**w+Ar^& zN*$lG>Fc*n4QBBa<25XJTot6QR`W`eWPw(@lffDGkb4lHmrNOWH4(dCk+%k zsE(EYpo{0edpRd>?!dW{7Rq<{d<*kAWq$VulbDFQE9=23&RzGr8pA6;166PQV&YF6 zvj8_!3@w~G{pmYj-oD-V)X?M}@9&SwT&VNGrv z-V<9sJ$1YRF7*B zCny0UFDhPOj0S>`H$^!XMuWW2092h9)JbxY{>wM^>vfV?q`%+dVqC#8P%l+Bhd@p9 z;##4(L9mPZh0bh8l!E~RK;?DOfL{)C(IR~;E(fo*W?HX{bx|9N^d;JWI*+SbSEhu3 z&SESy2uz_>XSEwyi`80Sov$-7tc7OGw3#uO%v=G(6_|91kKUOLuN)!K$GOYzP7DXF zB7ME8205B;Y-}_(nvF`Rj%Mw4JI$DAlgR*k7{X1m%0~@ycy>I7&^Z3MMZ$&lj*?;W{cS?`s@a?$QDA7z-F+stY8pW zU}da6o84rO(}V-11sgHYl8^-J67Nszqs>KxgRvG?uosFpL!pTg3_h{I2Pedu4HmY* z%X)1#Yk@@=p35j(sm9cpj#LdQQ~XNErHH_3cF?0W zkE((P%VFwKz=#xpAIbQdcX@m6?tgvVAod2St1MUut# z+q_84=-Z5n#3F+rK=3BPh3A8K2`&T(-Xys2d=M|eg#f{u1Q(tU;w88cAb6AD!t+7A z1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J5WGon;rSq5f(rqHHwi90AH+*=AwcjZ!G-68 zcnK~92;L;P@O%(2!G!?9n*Ggqz>3?Sf-mbbRplkM(U1Dui|*?!&VA(hocG<#mWO&jpY!y_WB(dB z+U7ku)YWyv57%$p8+q+l)80RLuus1%d-K*EkNx5M;VDLGu&et(N&Clh^0F6O7o7Xk zgmc~7=HIvO<6E!v4E}cd#4O5vFlW_dPiN_`d-xkGW}NumTp)bLTz6O5ysxslE}qw0!|?78}pp4+zWKD*g1FQNJa*Lu2pceYMimGew@=@ab5z-3FH|MeYqkq`*cjnB#ooA=rmfd!HXV-`S?r?40J8R8L3%W|%-FF2}I}R%=+@Gvpn?J|( z!R(IyeXh>-AKm)SKH2kG-LdTGtFvY^Z6~6KFI+x)?#;+RWGz*()bZrq+Yik@v}ygL z#&^D!+RpE9X!)wg@!kYq{?JX)-50p3&0m&WIY0RumU^!Cft{BgUD~g`j9XQ)xO`9P Hs)zmq-qc~w diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_left.png deleted file mode 100644 index 480358e6a1c6da1f527110a7605402f6b162c0ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15918 zcmeI3e^?Xe9l*~g(kbdzYO63?jZvqRCU=(*Le3Dx1SMNz`4KBns$A|aa2j$k2@ud> zk+#;^R%@-)U#Qz~6RWOWwR38rDz3UJ57kFIZHHL!*TmXci>=P=y@dSW1@7^A+CBT{ z?s<~C@B96A_j%v%_q~5^dFHH{BcgvEjUdPfQ-(1coFCzTLk5HYdw-kpIXDegtcCOxlwST-%39B!f9)MR9r`L74@*JT;2RA3y~ zjC{s4KONXeYTQ+6OqQ*3#`2|G>+9>6QopRqZkX<57MkWe-d$QW*Hx93R+VKn&5s7g zr+@koIOf*gdnBs+$S1legJkwYPu9Hh@sy}vT8`}qqPUdt7j5vGt8HEUVnC9Wd$0A zzcl$b8`i8}W3ZgIR5jG)JonqVJ7eD2{=4RFw=))PK575r$%9jVogj&;S@QDAS6?5q z8((M7XV%(pP^4o!-CT70{P*=IMw}m6HnnTc)K5w`UO9cOpfmfz`WvGzkG&Cd;c92+ zwXQ4FhZyU-;+>~FtTBOL(ysb z&V!cjt<{ZLyN~VGHZs%ywQX-gSKHM3{nz)uxrkVF_V)8#K2;X8x}{@v{H<+uZ-2|a zeEsQ`y!tMaTFxCUJ%6V0AAfv5 z-gWDUeDYr0@xi&Q1MaR}qZh6D_Gs6JJ-%~k?S|@QcUNvqIqLrWuCe==>JJqs?`+!S zyzyyN>x!7#O9fTV*`4`kZr|2?_|NzDeX;oR>6EQ^TYt7zxA^X7_e@`&QnfVCn|QFG z`&##yl^^7^rQ9#P^KSd8@6dnGyVcm`8-G)E_3YYI`;rOwci#7nLk1s`o*44`HLVCj zpsXd=lWTsOptuqRNpn_4;Vp53ib4=ws@F+U3mA{o$`rAV6xmn%8)Z_KPLa(~n=!M~ zz!b9?l`bZya+ZavTtI1QS*ku-=OsXZ62?PHy(M;soA9Q{{Ba3z%@3n8so%u2AVrqO zCzR%zGo=R3#YokPBsqm8X`~vh0#|D^iJGURN(@({m}73C@g=PE*RtyYU-N>r(o0}Hvk!r>vka)*0DAV@Hd zk#SQl*6Cq6hm?;?TDekBicH2Q3LQPqRpJaKa=3fg0g9-XbfUNdL;G{0sgRGe)MfYI z8cm^$ohe})9yjpA`}K1MyI9D*SIUrjz{Frq%;x@`gZEof(r;?FC%p_H=t0^mZP3$g zscT@;gE1|DTXe^2jWZQ;r36^Yjjc<+pt9l(v(1l;^fAuvNa@T`Qv#gHD( zW#KrxKCsHd8v)n{_*EsHGKY20T)8`5hr%x5`3^^8jHHLr1J7hRmMF*77ED8^wS-EY zfZ+s&MFa_0LNUx7&DtvZ#7HJEHKEo-#)zIlxN!lZ9_-CO(GL6zO9gFj~m-}Iv)%{Aby1Vz&Xl`8HZ4X215C*1`gupY@Tw`#iSL174jg> zBM`N>8ysGT^4p1>{U1z(n=Jcb)9_$b^+TosYI6U-X$Y@ks+e>XF|-~HF5uw6gUwuE zK6_VIuv+_8qK@C+2vEgfMUnpM0e!U3SU7XQK9{dXd#T@Eqr||0;{1P)Yor=>%@cNw zRKu=e&)E*vqX#|6f1}WFgcrXF2X=@Y@V?0ClhkUJT8F~Jutz5AW$d{|7W82^-%s%5 z9v@iIuVdaojuDC;$G}uYyxI9{Ffa<<8^J3p8hU5#Q=XwN_G4azpXkSo3dItWKtO~= z5tlF@NGsxkfC!5sE@3{9R>TDX5f(*U!h9gDhzkNDEQ+{<`9N9`7X(CD6mbdjfwUqn z2#ByK;u7WqX+>NR5MfcoCCmrXint&k!lH;vm=B~CaX~P5f=nRSQK#y^MSM?E(nOQDB=?418GHE5D;Ne z#3jrJ(u%ksAi|=EOPCL&6>&j8ghdgTFds-O;(~w(iy|&zK9E+#1pyHjMO?ysAgzcC z0wOF5;))J`1Xs-`J#p{OFqA4Md9!vvB*Tp X%)ui*+c=T`TGf<3%eZTL;nM#Ctaa;x diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png b/runelite-client/src/main/resources/net/runelite/client/plugins/grandexchange/arrow_right.png deleted file mode 100644 index 63ab23f273cfcdbd6ff203ecf37900db0eb23d00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15917 zcmeI3dvw#*8NkJH(m^QQ3@yZC1!G_(q_!kK;#fEi!4AZ24S5(yXn4r7oCs{o*iPaE z76>V&^Z|@g(((wUBWyr{wF5dz8G#a37?4pout7T>O?Zs}9fi=r@UHB{c5V_K&S`h{ zk93YLefN7wzk9#$>K`r69zU*6!p{>B1nFbWGUbAEU+&j44*Wm-^VqMzsh20Kz=t5c z`*FV>$f`Ao2qH>kjK=KjV%E?4idm1;Y&1$eUe>`lX#@$JrDn{iy>RtX(~)8I_1W3h z+@?WIi4w7-uD-Ii>aD8xs<>kazWa~$xU_x|ca5f_@AMGO-E*k^i~9O{Pr;cJ4Ts;U zQrE94%(fl?1M{n@_Kz68GZnaA>@5-*E6U9oy+k7FNs*{JE4MUs-GjR>&03}cIra6t9jTrzV+ZyeY{aL@rjYuuYEBh{=3^5uMHXPWpX61G;lZh{;_4B|GxUyOYt|p zeg5sFlQxI=vZ8ydj${3=-_sh;=h(l?h3+>XEJ zoI#g~3EJpD-AUv?%JCimMsb92!t=hnT} z*7a5Ua<(7duG>eC`RB%6$t~wc)$O^l=j}PfoHKW)wggo<^zz20dG*Fi zjRkdo7;s#d-r6{~B|fM3r~xg74`*)rPPwkRBspc<+>fW&-fBF1sN~$~eSiPmjwJ7$ zgYxHg;qFh)=6vRB**0L#ihGAzR__d+&A4c+TJ&J)nzTc{uO66M537D#e*FI0wVs<_ z#-DgYTywdw(lfDn%IUjzwSW4@$Gg9td*xKx`UfX|x>7&)!QUR5zd5ODY?wZ5Z(-~8 z)`3eu$vdC+XvY1IE}r}z{nzw6`&xoSZmX`HS()yfH}uigN5R2J+yUvao*P!2KoA0D ztoiZY_i6JSv*F% zn8~W}(s>o*tyIM0+-6`{rmwNl!4$P5YKO7FEh zh+Naywsb&BlNI~@9s)(n%E}aFDh2B;LUEl=hhj=psgwf?xv$*qCj)Y~Z)iA3dmIz( zqr8mA&#-PO7niiLCH^#-j7tkV=V|X^5%&%$BkFDwgE_HSx^`~AUze-P)INV^DL~MMv_sl}dl%ZWcy`OPJC#ORsPSfqZ(*dR%H%=3F3oV844C%qM5=Jjg`dP1) zWu1obDvNFeU?1RCm2|{p#%*WId`Wr~c8Sh+G#YIp{j>pirpht39M@PeEuqm7Dorwm z6Brf~By0)Au&{Q$j7dz0&|)Rb zZl9lYQ?%J+00Rn!u@iP&n`%=kZE{-Wu*q?oS|cY(wOX!G*(jTaN~P>7Dl`ukORRgR z&?c5D;R+6f4)-QIOM&pwCsR>Mt&L8_zAm2d1&>?3mI4V24q35vEhB zEXD^Ge0fYEfN}^m$2@v=$I*6$lL<9B59#&MTnVSi+G{w{6(dcL^9i*Af~2_iXrQxC@ZGjd=(lF6|(9+h>vyn%SbPsQ3O`V<1~+O z)Q)a&cs_owqzx$=jY^|O;bGV#n+ed)d=mruu#f8}cxsyu ztmx9QpgYGHMVn)Is$$;kLNyp31@DdEl@*P=vvw-aNEiDtFQQNMV@8Ey2}vLz{Gx!1 zpAV!Ja6v%$MFAH-A4n_Uf`IUg0xo_&kXFD20pS+~T>N|>t$+&x!Y>NA`1wFu0T%>> zUlefh^MSMiE(i#}DB$Af18D_Z5Dvd7_(cI1KOaad z;DUhgivliwK9E+x1p(m~1zh}mAgzE40>Uo}xcK=%S^*aXgkKbJ@$-SS0xk#$zbN41 z=L2a4To4d`QNYE|2hs|-ARzprfQz3Gq!n;MK=?%g7e60JE8v2F@QVU2em;;^zy$%} z7kP0dM85*1-Qd&DGVqP(1kKS8!S|t3Dl6B5AQi(AWPUY*d>;hos|b?Y6Y23xKKQh> z20;d}XQm8Z25hZnQ-(Eg)^@*fbfWUf4YG|vWl+*9sqdp(x2F%xlYFX>IFDdc=VwY% z79IId+5PPcr;G2*`#g9rnE7qefe+%QiYE=f`g&aO9656TjwPxnk$q diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/add_hover_icon.png deleted file mode 100644 index 9eb7fe3baa3af0e9cb5ddf933f842503ab45f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15845 zcmeI3X>8m?6vro&qXbe_Py|#QW;v7|UfXMX4|lUonk^)VZI>iKTBNF8dp0rMwO!lk zZbGRl3P{{VoZ^z8TrIQ$aR`a4P*hRzfwL4)IaCDY<^!q#afI>SJ4r^BC_+fH(ysT- zoBtcnZ{9rHAAD_k$$4$9vs(cGZM{9oK6ISnf2SXbeoyaV-$93??Ve>W0LL8fe_O!a z_nrhmOW5e{PN#=0&vJ(?o9XTDW^BjG8aW6+@#C#GJ$3$$^sLUeu77Y}dfwry?c%NN z?azI?a_2+r;+aqEK4aGh4?S?caoma(yVqT{>C&T4EWEOP*M)ZakHf}0SAYBY%riD@ zn125^dv|>4y!zOOBaf=LsWVsYHkEe|td0Ht`mz1s?W3Q5-+Xok*mu@VzrMPE-eI4Q zzPD{b+h04^-Zi)Fg7rU`w_VN5?Hph9=t?J^TwZT z?Q{10+_e^D-#W0C{|bn2TyX?H;%#oZtXJt9da0j%{0`Xr!aaxI5?p{Y1qFI#r`cYlc6_P@FJ)0=0m9~_&%yem4e|FgL_-1pi3eLIUM z&#=_7zGps}_xwleum0}AtrtJ=;t{`m!EN8U^v|!CFUKu$plDV;sOnY* z28($c@dhBiuxP8=D(Ep8IAoX|fuCM@CBPVZN8l1U#i#6UIBfKcIk10h$$&PtN{i`% zg^AX9Q9%Ln&{LUWK4-d0u_I85tDygWGZ$b=UA$Etfi8bQW-ygzx-ADXaxlzld^o~H zVnIQUM5M@kCd3Oe$BUfEvmsHDctr{^l}{kiivGnNJ*)I3&#erHtd792=h+I!6$*u5 zK@3{X5GTZ9F^&&$p%9CDuj(t=foh-1 zyz+K!Ak(d|Lln89YI8!6=cW*)Qe;^^KdI977Oz2PQkmJL1G)pFHst!CYmGP>T)YOF z-kFn~gs#=%*dtD^#7EaSn1gvFanZ>Olb(WI-Dov=(`nS)Q_kKqvh~Gj()yk$Ww2;W zA@eW0a=8cpOVNvZDtT zrQWkSMk=NBny#ms8thFb(A|RuEv6Rl{k7V_X6pcw?wKl{gVI~aK zW4I`rMjNsWM;t#&?KdQIaI0F)InS;>lIW7b`7BnIhdVUj@m54 z!^b(lR_Ba=VIf>~%|BZPLE^QIl ztPQK?5Y!V~b!OFu|I^AHTs`~;8wjub|J?>UWGh$RiOn^!TFNKZ(73KCStY=HSe)Z_Ka+Z^_beKRDe6r?ARp`+mU4AmC6rH~F7BTJUt}u$O{`Dvg z-5SF9cP9$EOrVmbmddjb=O(n_{RjPWpBpLlkA!&i;ROG;B5icz(q&|6qZ^mjl^2ugUY{!Qc6tw(cL~wgk@Qj;~d_ME7C?0<_41ny3o7+1xt`B8r(6daM@JC z21i9$&Zvg_@=}p6x1zg#^uWQ@o;W7d|Jr5pQ2wdU=uk$*V$ne`AY_x`BJ;t#6c+}B zY*JigKA4x{!hn!Xii^w#^HN+G5VA>ek@;X=iVFimHYqMLAIwW}VL-?x#YN_Wc_}Uo z2-&2#$b2v_#f1SOn-mwB59Xz~Fd$@;;v)0Gyc8D(gltk=WImXe;=+KCO^S=m2lG-~ z7!a~aagq68UWy9?LN+NbG9S!KabZBnCdEbOgLx?~3<%kzxX64kFU5rcA)6EznGfcr zxG*4Olj0)t!Mqd~283)JkWGq<%m?#QTo@3tNyOD!e}@;E=*8UvdP8?z z*D3d*w|g0_r!NJ-*x3NA-vGdG-hM>Y2oifJ2rm;ujyY99KGhk8;jCwvo>D0Yfp6csnXliQRngTY2W}e?dK;~u6_=g Opm*_-?G#0I#0H~DcmZSRG@w+_;$ z;blo~aEnP%?^|krTc~e7ww$6`d}<_;NQ~;1ZjS0XM=TO?a{qr09@ ze6at;FFkQ7(YttOuJF~Ko}a$H{p6GM`mU$Xu6*J5Pwu}Xc~Kez5+_lf1~^nFhaTxj{8 zpq}dwQQzJ62GhUl)LR`rtIk~=*i9)fUfa$7nev>vx0M~Y4z=uv3CZ+xL-Y^64r0%K zYw;tYj{%`uHr947{n{s=I&i@C@Z3$uQ@=c~3C7_aTRwN?P0x|bzq;_pFLzCiOs?5F z5E#Dv>gtES{p#gQCkr3x)WylnY$WWOI-=)U7S#L}?6Ijuc3%TA%Z8&mMC+RJqT&nUAyaTm+1& zgOdgrn%p=nO^!)H*||2{9x4dXK^9n|qma#Lrcmf}7X1qFzun9@9mOuzSf6vi9?&ro zPdFmF0UW%`M@y{F@9+m*Zr<Kx^i0B_|a>QD91jnzK}0 zbJ%{xls<0tIi2=E)lYd{*<5uX%`CHn6q$mUW85y5S->bB$D3udO)X7p{Vq6@^2(YH zXbw;00Fwl!K5j^0{Vt$cw>6uDELHvF#*Ivok1R1D1G3P>gp+qSokFg1(5mJQ)2ON! zT)m|#wasa;wKWTxfr7e#nXM_A6+@x4Lm?P{Lc{=~r5nS#o(Y$ttd=HVmawDB(X(0A zWIb=L3NffjZNF>YKvc9q7+MBsmWRPO%=!g4Cvbc(>lRqH&Pk~!;v)`YUluJ9_2Lc+ zUXS2u@Y2v1^`pzGGI3pAYCRi$#N$FtGc8e*Kr9-DYj>%tEO?Zl+avh`v|9o}Iw*S; zIsj4unoCJOMfA8uS(1y3Y&4(eI;ucq#+cIh!}oCPRgJNzBysvDYHQ!4$7o=KP$Wb}Mih9P9PbDL?O_ zfjcGgK3Vbu2~>t?;%44ZgQ^LmX`(L5V5}80&k)$z*jiC!)M5t7iG~U6gW2b-oXl#< zsb&=0rg*>wQL=MSShB+xK->+jwP{r?%Vz5Q?Yuq&CI5Bi3y+Z>1sX2=ORkE&7ItFl zij@})Fp!23ay=)r2OD9EyWZJ0$PxfvDHx!ATtJ~^8Ac*JOFSL$ z1UZHE@sjB0D%<=n--cQgOQWKe26C9GtgO=TZrZu)%ZKma0HK-x|960H*v^$sVq;FM z{ zo#cjfjvjbd7=YEk=B2J%Q#X}TGXP7cT@lR2lvAt->%??pt4+6@pqdv%o|Re7EBRnS z#XcML!lTdL+~L6gnc~k;X@4Cj9Mhr0?3R+ zRe1km+I86NE!&`qriNPJAfL9_%H0t7D-TzEc+mf%8w;6;K9&j-;GTnG@n zNO0l#AX4T@FKy5=YwboE(8c(B)IT=5G}!l0Ktm{7oHEICAbhE zcoB=Mz4mG{(BS*XdHCY-spK66_|mdN8cfD1YVs2lH8o99|DJ=_S&B-wQ7s=Dp(x=| zidv?h+j{H6u(m z^~x@8X#c^3)7x)8+8X&yu&{w`Q)YV4gJo;>oc{csS6*&4yP`A72M)Ea*!OgL>51eQ z?pe7c)yj{(@x#Zv^}WyR>Ad=#;O2{qK6=Zs_q^?zdGO4&#!pS?2MId$=+6~Bo6 f{od|>%uy@yh1u5*_1|v4c^zB7F?wX(_C5avjhnx~ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/screenmarkers/confirm_hover_icon.png deleted file mode 100644 index e5b719965807d8ea42a5758b5d6995652f3612b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15951 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 z zplb+b$hqvyLeyFr8a~82wK}E!4;I3bH-5ThP!#~YN-)6qgg}x}RhWsg2kxqXCnzL2 zpQy-wp|s4O@nxuFu`(>{LqH9)rJ0o)K1(ZiZuRg}ZlLPQ|Nq-S=WXQ*JF&JVmg?;6 z8X8g+DQTE#*$!)Ic?hr>eJI3M8Y|fns+=AXBW0K|Lj_@YNYrbttAc+GdJF#yibW^B zVj0~E?+OF(^shvz>Q)uTxjT{IF#$`KoG;IOh@I7j_8)M{eQGq{-!E|R#q!Q)QCjUr zrGt2BwHuXIlsD;`9fqYPf1yZomCCt=lJZRZ;N3n5`$W+Lk9o8iRrG2(kQ#_;@SwMx z1B(w7HK?PmaBwbRm7^#uWK`9CdA`UOTH#$k{KLVP|8dN!|K(%yeEz7+=zK;+Vv#`* zAb6AD!t+7A1Q!AXZxUR1K8Tm#LV(~+f(y?F@e*7J5WGon;rSq5f(rqHHwi90AH+*= zAwcjZ!G-68cnK~92;L;P@O%(2!G!?9n* z*IYRX`+mPC+SQ*s_VOEhnoNXHtM+`i_@hI`{9x9{2d zr)`bgyld`w*rR@7^zd`@xy9MWcT>-Ay-VA3%P?aQtCIk-rFM@cev91o|b5G mPwl1;{Qf(B{0sgQ#=rN1O6cZmt8V}u$KdJe=d#Wzp$Pyg3Sg`N diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/components/search_darker.png b/runelite-client/src/main/resources/net/runelite/client/ui/components/search_darker.png deleted file mode 100644 index 0d5d4608a6159a00069e0ec95c70b7dba1bdbeaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16280 zcmeI3e{dAl9l$pT0;yp#AP5ZA<4_62z1{o0w_c8fyJ%uLIFnct+F{t+eU~iU-7a^V zntu!5lRxy2hzwhPphH2?^ zoY|YX+xx!n`+mRsKJWX!@9iJ^sBhuDMTOG~34$o9uJY8t>lFB%I_^gJ`_+M>Z^6rW zxvC*b5Z{=jehY}L+olpkflYL~eZD41k)lnKtgCjrb#g=sh(SOQu@85zf91ZzzMIRB zKi=8zD;>Q;jy+XU^4iBs`<^E&i(fu9^XU7}@4Qc(xMazxRS$Q4XY4Jl2fL5nFZ;e6 zHB`Rh<8#F`*Q^=0<1c3ppN|}R@q^}F+$OGg`Kd7b-e)TvU-aHoOT0V&)sx|0Od$Gi zUw`G$K-W2haVMp)VJTYxPqx0I6L== z?Pmx2`(o23NL+i(zTcO=@!O8I7dv-EEEh!*;tuA?w5uC)n-bsi+53$m+33ifVz6GPv@2*KxZ}B zNS?OYb#{lrXtmod_PcZ@+GwR{Gi9bplbN;9ti_~De)O(F_|F*;0&I=vo@6*EmFb%l zMP@0gwYAmIYBorbM#|`LI4IggnM@??K}Orc3Kt{8(OC&6DL)<%W8D$JDN>|Pl7g;8mSxHWtP*Ng=}I0H!-CWro#UiX zlgxZ)ya5lV02j1mNZLXg={h6BniMS->W1tT!pInrX{0;I)gF_H`~ z_({QG;(>!VT0nq_4>H$$m}`&3H>-I7T_+a4Ao0+BI;~im30O@wBTbrlU?Yu2z>t0# zI7m*g1c1ZB0A5JYgn_c04GXj=BEq`F1+)67V-a95zs1U18H=BUzYaK8mH_GS3k=Bs z4ls<>Y_mG7sj=j7Gb~h<7=_u?mX&2N*W#6@7Oco^O(}w6yn@JFBns4pS*A~|%yh{~ zR}|GIzQ9?IS8I=pSJM{=PAaE0b6aW4q?tH;Irs@cRTw60h^~xyIa9m07av0H35vN9v&VzG zU#Gyc0VxvV6qgv{8UZDT8=X|9F$;it=wjjS2P-ib-*t>mYFHcEzh2$Ng3a;%b`uRh zlu`Xnnr5p}(+0e0wi-3fSUwOI6_+U)4QbA@Qcs3#JgI8oLkw`)tX4DJ@zG|~!Y9T+ zu-+rW^Gj6ShfOe2+y+(THEhT-v}7A*DUybXDb9M_h$mN~6+XGl12wv21;rSq1 z4Hp6gFKW2(d=RaM3ju-`HC%W;h*rae0KtnIE<7JZtKmX`;6)7=o)4nca3MhOqJ|64 z2hnP{5FmI_!-eOAXf<325WJ}2!t+728ZHC~Ues{m`5;;i7Xkz?YPj%x5Uqv_0fHAb zTzEc+R>Oq=!HXI$JRd}>;X;7mMGY6852Dp@Awck=h6~RJ(Q3F5Ab3&3h3A84HCzY~ zyr|*A^Fg#4E(8c(#NsN!FD>i%sv0jrwBJDx9cu{UpF{9^ zfgoze5e1X$34;AGLEIvpUVPgI`1bfs)t-vF*oU8-e7kh*{WHd1I{wt3T8iqcN~Zko zS4+EB`{iRT#|(oo>wCvIKw_-y6jIX`5Mj~dwf zNaH7+Gu91msia<$w@w}bxmQu@%v@v~#|Z2iohlatm>sXOrUm7%Vo eyDwcNh&9jt=JkVbj8@VE?+3J!<> diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/info/arrow_right.png b/runelite-client/src/main/resources/util/arrow_right.png similarity index 100% rename from runelite-client/src/main/resources/net/runelite/client/plugins/info/arrow_right.png rename to runelite-client/src/main/resources/util/arrow_right.png diff --git a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java index a1caa7f1ae..1156557160 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java @@ -36,6 +36,7 @@ import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.util.Arrays; import javax.annotation.Nonnull; +import org.apache.commons.lang3.ArrayUtils; import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -66,9 +67,17 @@ public class ImageUtilTest BLACK_PIXEL_BOTTOM_RIGHT.setRGB(1, 1, BLACK.getRGB()); } + @Test + public void bufferedImageFromImage() + { + final BufferedImage buffered = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + assertEquals(buffered, ImageUtil.bufferedImageFromImage(buffered)); + } + @Test public void grayscaleOffset() { + // grayscaleOffset(BufferedImage image, int offset) assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), -255))); assert(bufferedImagesEqual(oneByOne(new Color(50, 50, 50)), ImageUtil.grayscaleOffset(oneByOne(BLACK), 50))); assert(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(BLACK), 128))); @@ -76,6 +85,73 @@ public class ImageUtilTest assert(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(BLACK), 255))); assert(bufferedImagesEqual(oneByOne(new Color(200, 200, 200)), ImageUtil.grayscaleOffset(oneByOne(WHITE), -55))); assert(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 55))); + + // grayscaleOffset(BufferedImage image, float percentage) + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 0f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 1f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 2f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(GRAY), 0f))); + assert(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(GRAY), 1f))); + assert(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(GRAY), 2f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(WHITE), 0f))); + assert(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(WHITE), 0.503f))); // grayscaleOffset does Math.floor + assert(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 1f))); + assert(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 2f))); + } + + @Test + public void alphaOffset() + { + // alphaOffset(BufferedImage image, int offset) + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), -255))); + assert(bufferedImagesEqual(oneByOne(new Color(0, 0, 0, 50)), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 50))); + assert(bufferedImagesEqual(oneByOne(BLACK_HALF_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 128))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_HALF_TRANSPARENT), -255))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 255))); + assert(bufferedImagesEqual(oneByOne(new Color(0, 0, 0, 200)), ImageUtil.alphaOffset(oneByOne(BLACK), -55))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.alphaOffset(oneByOne(BLACK), 255))); + + // alphaOffset(BufferedImage image, float offset) + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 0f))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 1f))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_TRANSPARENT), 2f))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_HALF_TRANSPARENT), 0f))); + assert(bufferedImagesEqual(oneByOne(BLACK_HALF_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK_HALF_TRANSPARENT), 1f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.alphaOffset(oneByOne(BLACK_HALF_TRANSPARENT), 2f))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK), 0f))); + assert(bufferedImagesEqual(oneByOne(BLACK_HALF_TRANSPARENT), ImageUtil.alphaOffset(oneByOne(BLACK), 0.503f))); // opacityOffset does Math.floor + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.alphaOffset(oneByOne(BLACK), 1f))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.alphaOffset(oneByOne(BLACK), 2f))); + } + + @Test + public void grayscaleImage() + { + final BufferedImage[] grayscaleColors = new BufferedImage[] { + oneByOne(WHITE), + oneByOne(GRAY), + oneByOne(BLACK), + oneByOne(BLACK_HALF_TRANSPARENT), + oneByOne(BLACK_TRANSPARENT), + }; + final BufferedImage[] nonGrayscaleColors = new BufferedImage[] { + oneByOne(RED), + oneByOne(GREEN), + oneByOne(BLUE), + }; + + for (BufferedImage image : grayscaleColors) + { + assert(isGrayscale(image)); + } + for (BufferedImage image : nonGrayscaleColors) + { + assert(!isGrayscale(image)); + } + for (BufferedImage image : ArrayUtils.addAll(grayscaleColors, nonGrayscaleColors)) + { + assert(isGrayscale(ImageUtil.grayscaleImage(image))); + } } @Test @@ -145,6 +221,15 @@ public class ImageUtilTest assert(bufferedImagesEqual(twoByOneLeft, ImageUtil.rotateImage(twoByOneLeft, Math.PI * 2))); } + @Test + public void flipImage() + { + assert(bufferedImagesEqual(BLACK_PIXEL_TOP_LEFT, ImageUtil.flipImage(BLACK_PIXEL_TOP_LEFT, false, false))); + assert(bufferedImagesEqual(BLACK_PIXEL_TOP_RIGHT, ImageUtil.flipImage(BLACK_PIXEL_TOP_LEFT, true, false))); + assert(bufferedImagesEqual(BLACK_PIXEL_BOTTOM_LEFT, ImageUtil.flipImage(BLACK_PIXEL_TOP_LEFT, false, true))); + assert(bufferedImagesEqual(BLACK_PIXEL_BOTTOM_RIGHT, ImageUtil.flipImage(BLACK_PIXEL_TOP_LEFT, true, true))); + } + /** * Compares whether two {@link BufferedImage}s are equal in data. * @@ -187,6 +272,32 @@ public class ImageUtilTest return true; } + /** + * Returns whether a {@link BufferedImage} contains only grayscale pixel data. + * + * @param image The image to be checked. + * @return A boolean indicating whether all of the given image's pixels are grayscale. + */ + private boolean isGrayscale(final @Nonnull BufferedImage image) + { + for (int x = 0; x < image.getWidth(); x++) + { + for (int y = 0; y < image.getHeight(); y++) + { + final int color = image.getRGB(x, y); + final int red = (color & 0xff0000) >> 16; + final int green = (color & 0xff00) >> 8; + final int blue = color & 0xff; + if (red != green + || green != blue) + { + return false; + } + } + } + return true; + } + /** * Creates a {@link BufferedImage} of a 1-by-1px image of the given color. *