From d6e465877ee7c1dbfbcc9a4ddf5046e035e8df70 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Sun, 29 Jul 2018 19:38:23 -0700 Subject: [PATCH] clan manager: Use Sprite IDs for rank badges This commit adds `ImageUtil::resizeCanvas`, `ImageUtil::outlineImage`, and `ImageUtil::fillImage`, and applies these functions to the clan manager to replace the clan rank images with calls to `SpriteManager`. --- .../net/runelite/client/game/ClanManager.java | 89 +++++------ .../net/runelite/client/util/ColorUtil.java | 10 ++ .../net/runelite/client/util/ImageUtil.java | 140 ++++++++++++++++++ .../client/game/Captain_clan_rank.png | Bin 2986 -> 0 bytes .../client/game/Corporal_clan_rank.png | Bin 2854 -> 0 bytes .../runelite/client/game/Friend_clan_rank.png | Bin 2944 -> 0 bytes .../client/game/General_clan_rank.png | Bin 2997 -> 0 bytes .../runelite/client/game/JMod_clan_rank.png | Bin 205 -> 0 bytes .../client/game/Lieutenant_clan_rank.png | Bin 2965 -> 0 bytes .../runelite/client/game/Owner_clan_rank.png | Bin 2893 -> 0 bytes .../client/game/Recruit_clan_rank.png | Bin 2845 -> 0 bytes .../client/game/Sergeant_clan_rank.png | Bin 2869 -> 0 bytes .../runelite/client/util/ColorUtilTest.java | 22 +++ .../runelite/client/util/ImageUtilTest.java | 90 +++++++++++ 14 files changed, 303 insertions(+), 48 deletions(-) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Captain_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Corporal_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Friend_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/General_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/JMod_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Lieutenant_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Owner_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Recruit_clan_rank.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/game/Sergeant_clan_rank.png diff --git a/runelite-client/src/main/java/net/runelite/client/game/ClanManager.java b/runelite-client/src/main/java/net/runelite/client/game/ClanManager.java index 3d35edac8d..53908086a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ClanManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ClanManager.java @@ -28,16 +28,17 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.eventbus.Subscribe; +import java.awt.Color; +import java.awt.Dimension; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.WritableRaster; -import java.io.IOException; import java.util.Arrays; import java.util.Objects; import java.util.concurrent.TimeUnit; -import javax.imageio.ImageIO; +import javax.annotation.Nonnull; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; @@ -46,26 +47,33 @@ import net.runelite.api.ClanMemberRank; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.IndexedSprite; +import net.runelite.api.SpriteID; import net.runelite.api.events.ClanChanged; import net.runelite.api.events.GameStateChanged; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.Text; @Singleton @Slf4j public class ClanManager { - private static final String[] CLANCHAT_IMAGES = + private static final int[] CLANCHAT_IMAGES = { - "Friend_clan_rank.png", "Recruit_clan_rank.png", - "Corporal_clan_rank.png", "Sergeant_clan_rank.png", - "Lieutenant_clan_rank.png", "Captain_clan_rank.png", - "General_clan_rank.png", "Owner_clan_rank.png", - "JMod_clan_rank.png" + SpriteID.CLAN_CHAT_RANK_SMILEY_FRIEND, + SpriteID.CLAN_CHAT_RANK_SINGLE_CHEVRON_RECRUIT, + SpriteID.CLAN_CHAT_RANK_DOUBLE_CHEVRON_CORPORAL, + SpriteID.CLAN_CHAT_RANK_TRIPLE_CHEVRON_SERGEANT, + SpriteID.CLAN_CHAT_RANK_BRONZE_STAR_LIEUTENANT, + SpriteID.CLAN_CHAT_RANK_SILVER_STAR_CAPTAIN, + SpriteID.CLAN_CHAT_RANK_GOLD_STAR_GENERAL, + SpriteID.CLAN_CHAT_RANK_KEY_CHANNEL_OWNER, + SpriteID.CLAN_CHAT_RANK_CROWN_JAGEX_MODERATOR, }; - - private int modIconsLength; + private static final Dimension CLANCHAT_IMAGE_DIMENSION = new Dimension(11, 11); + private static final Color CLANCHAT_IMAGE_OUTLINE_COLOR = new Color(33, 33, 33); private final Client client; + private final SpriteManager spriteManager; private final BufferedImage[] clanChatImages = new BufferedImage[CLANCHAT_IMAGES.length]; private final LoadingCache clanRanksCache = CacheBuilder.newBuilder() @@ -74,7 +82,7 @@ public class ClanManager .build(new CacheLoader() { @Override - public ClanMemberRank load(String key) throws Exception + public ClanMemberRank load(@Nonnull String key) { final ClanMember[] clanMembersArr = client.getClanMembers(); @@ -92,27 +100,13 @@ public class ClanManager } }); + private int modIconsLength; + @Inject - private ClanManager(Client client) + private ClanManager(Client client, SpriteManager spriteManager) { this.client = client; - - int i = 0; - for (String resource : CLANCHAT_IMAGES) - { - try - { - final BufferedImage bufferedImage = rgbaToIndexedBufferedImage(ImageIO - .read(ClanManager.class.getResource(resource))); - clanChatImages[i] = bufferedImage; - } - catch (IOException e) - { - log.warn("unable to load clan image", e); - } - - ++i; - } + this.spriteManager = spriteManager; } public ClanMemberRank getRank(String playerName) @@ -138,10 +132,9 @@ public class ClanManager @Subscribe public void onGameStateChanged(GameStateChanged gameStateChanged) { - if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN + if (gameStateChanged.getGameState() == GameState.LOGGED_IN && modIconsLength == 0) { - // this is after "Loading sprites" so we can modify modicons now loadClanChatIcons(); } } @@ -154,25 +147,19 @@ public class ClanManager private void loadClanChatIcons() { - try - { - final IndexedSprite[] modIcons = client.getModIcons(); - final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + CLANCHAT_IMAGES.length); - int curPosition = newModIcons.length - CLANCHAT_IMAGES.length; + final IndexedSprite[] modIcons = client.getModIcons(); + final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + CLANCHAT_IMAGES.length); + int curPosition = newModIcons.length - CLANCHAT_IMAGES.length; - for (BufferedImage image : clanChatImages) - { - IndexedSprite sprite = createIndexedSprite(client, image); - newModIcons[curPosition++] = sprite; - } - - client.setModIcons(newModIcons); - modIconsLength = newModIcons.length; - } - catch (IOException e) + for (int i = 0; i < CLANCHAT_IMAGES.length; i++, curPosition++) { - log.warn("Failed loading of clan chat icons", e); + final int resource = CLANCHAT_IMAGES[i]; + clanChatImages[i] = rgbaToIndexedBufferedImage(clanChatImageFromSprite(spriteManager.getSprite(resource, 0))); + newModIcons[curPosition] = createIndexedSprite(client, clanChatImages[i]); } + + client.setModIcons(newModIcons); + modIconsLength = newModIcons.length; } private static String sanitize(String lookup) @@ -181,7 +168,7 @@ public class ClanManager return cleaned.replace('\u00A0', ' '); } - private static IndexedSprite createIndexedSprite(final Client client, final BufferedImage bufferedImage) throws IOException + private static IndexedSprite createIndexedSprite(final Client client, final BufferedImage bufferedImage) { final IndexColorModel indexedCM = (IndexColorModel) bufferedImage.getColorModel(); @@ -228,4 +215,10 @@ public class ClanManager resultIndexedImage.getGraphics().drawImage(sourceBufferedImage, 0, 0, null); return resultIndexedImage; } + + private static BufferedImage clanChatImageFromSprite(final BufferedImage clanSprite) + { + final BufferedImage clanChatCanvas = ImageUtil.resizeCanvas(clanSprite, CLANCHAT_IMAGE_DIMENSION.width, CLANCHAT_IMAGE_DIMENSION.height); + return ImageUtil.outlineImage(clanChatCanvas, CLANCHAT_IMAGE_OUTLINE_COLOR); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java index f09cac21c9..7cd6ae0e1a 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java @@ -112,4 +112,14 @@ public class ColorUtil { return String.format("%06x", color.getRGB() & 0xFFFFFF); } + + static boolean isFullyTransparent(final Color color) + { + return color.getAlpha() == 0; + } + + static boolean isNotFullyTransparent(final Color color) + { + return !isFullyTransparent(color); + } } 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 13828dad52..22b6d8b671 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 @@ -24,6 +24,7 @@ */ package net.runelite.client.util; +import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.AffineTransform; @@ -32,6 +33,7 @@ import java.awt.image.BufferedImage; import java.awt.image.RescaleOp; import java.util.Arrays; import javax.swing.GrayFilter; +import java.util.function.Predicate; /** * Various Image/BufferedImage utilities. @@ -178,6 +180,26 @@ public class ImageUtil return ImageUtil.bufferedImageFromImage(resized); } + /** + * Re-size a BufferedImage's canvas to the given dimensions. + * + * @param image The image whose canvas should be re-sized. + * @param newWidth The width to set the BufferedImage to. + * @param newHeight The height to set the BufferedImage to. + * @return The BufferedImage centered within canvas of given dimensions. + */ + public static BufferedImage resizeCanvas(final BufferedImage image, final int newWidth, final int newHeight) + { + final BufferedImage dimg = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); + final int centeredX = newWidth / 2 - image.getWidth() / 2; + final int centeredY = newHeight / 2 - image.getHeight() / 2; + + final Graphics2D g2d = dimg.createGraphics(); + g2d.drawImage(image, centeredX, centeredY, null); + g2d.dispose(); + return dimg; + } + /** * Rotates an image around its center by a given number of radians. * @@ -229,6 +251,124 @@ public class ImageUtil return out; } + /** + * Outlines non-transparent pixels of a BufferedImage with the given color. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @return The BufferedImage with its edges outlined with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color) + { + return outlineImage(image, color, ColorUtil::isNotFullyTransparent, false); + } + + /** + * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize + * the given image for outlining. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} + * @return The BufferedImage with its edges outlined with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition) + { + return outlineImage(image, color, fillCondition, false); + } + + /** + * Outlines non-transparent pixels of a BufferedImage with the given color. Optionally outlines + * corners in addition to edges. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @param outlineCorners Whether to draw an outline around corners, or only around edges. + * @return The BufferedImage with its edges--and optionally, corners--outlined + * with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Boolean outlineCorners) + { + return outlineImage(image, color, ColorUtil::isNotFullyTransparent, outlineCorners); + } + + /** + * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize + * the given image for outlining. Optionally outlines corners in addition to edges. + * + * @param image The image to be outlined. + * @param color The color to use for the outline. + * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} + * @param outlineCorners Whether to draw an outline around corners, or only around edges. + * @return The BufferedImage with its edges--and optionally, corners--outlined + * with the given color. + */ + public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition, final Boolean outlineCorners) + { + final BufferedImage filledImage = fillImage(image, color, fillCondition); + final BufferedImage outlinedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + + final Graphics2D g2d = outlinedImage.createGraphics(); + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + if ((x == 0 && y == 0) + || (!outlineCorners && Math.abs(x) + Math.abs(y) != 1)) + { + continue; + } + + g2d.drawImage(filledImage, x, y, null); + } + } + g2d.drawImage(image, 0, 0, null); + g2d.dispose(); + + return outlinedImage; + } + + /** + * Fills all non-transparent pixels of the given image with the given color. + * + * @param image The image which should have its non-transparent pixels filled. + * @param color The color with which to fill pixels. + * @return The given image with all non-transparent pixels set to the given color. + */ + static BufferedImage fillImage(final BufferedImage image, final Color color) + { + return fillImage(image, color, ColorUtil::isNotFullyTransparent); + } + + /** + * Fills pixels of the given image with the given color based on a given fill condition + * predicate. + * + * @param image The image which should have its non-transparent pixels filled. + * @param color The color with which to fill pixels. + * @param fillCondition The condition on which to fill pixels with the given color. + * @return The given image with all pixels fulfilling the fill condition predicate + * set to the given color. + */ + static BufferedImage fillImage(final BufferedImage image, final Color color, final Predicate fillCondition) + { + final BufferedImage filledImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < filledImage.getWidth(); x++) + { + for (int y = 0; y < filledImage.getHeight(); y++) + { + final Color pixelColor = new Color(image.getRGB(x, y), true); + if (!fillCondition.test(pixelColor)) + { + continue; + } + + filledImage.setRGB(x, y, color.getRGB()); + } + } + return filledImage; + } + /** * Performs a rescale operation on the image's color components. * diff --git a/runelite-client/src/main/resources/net/runelite/client/game/Captain_clan_rank.png b/runelite-client/src/main/resources/net/runelite/client/game/Captain_clan_rank.png deleted file mode 100644 index 396ec708c9e5bfe4f4956c3418d8253add09a0d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2986 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 diff --git a/runelite-client/src/main/resources/net/runelite/client/game/Corporal_clan_rank.png b/runelite-client/src/main/resources/net/runelite/client/game/Corporal_clan_rank.png deleted file mode 100644 index 197b1ad4696ec105e6f646f8e969abc9b4de4808..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2854 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 Eg06^4H~;_u diff --git a/runelite-client/src/main/resources/net/runelite/client/game/Friend_clan_rank.png b/runelite-client/src/main/resources/net/runelite/client/game/Friend_clan_rank.png deleted file mode 100644 index ab2d17e1567fd0ee8358c680ba0b57f27d9d1491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2944 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 LNkvXXu0mjfulj-> diff --git a/runelite-client/src/main/resources/net/runelite/client/game/Owner_clan_rank.png b/runelite-client/src/main/resources/net/runelite/client/game/Owner_clan_rank.png deleted file mode 100644 index 96f259592a0b36ff079044ecd9a83514ae70424b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2893 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 diff --git a/runelite-client/src/main/resources/net/runelite/client/game/Sergeant_clan_rank.png b/runelite-client/src/main/resources/net/runelite/client/game/Sergeant_clan_rank.png deleted file mode 100644 index d85228c07358696c382181fc0e405a943b77a721..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2869 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 TS8kRN00000NkvXXu0mjfpj=Qj diff --git a/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java b/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java index d5c7530c51..3697dac28c 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java @@ -96,4 +96,26 @@ public class ColorUtilTest assertEquals(hex, ColorUtil.colorToHexCode(color)); }); } + + @Test + public void isFullyTransparent() + { + for (Color color : COLOR_HEXSTRING_MAP.keySet()) + { + assert(!ColorUtil.isFullyTransparent(color)); + } + assert(ColorUtil.isFullyTransparent(new Color(0, 0, 0, 0))); + assert(!ColorUtil.isFullyTransparent(new Color(0, 0, 0, 1))); + } + + @Test + public void isNotFullyTransparent() + { + for (Color color : COLOR_HEXSTRING_MAP.keySet()) + { + assert(ColorUtil.isNotFullyTransparent(color)); + } + assert(!ColorUtil.isNotFullyTransparent(new Color(0, 0, 0, 0))); + assert(ColorUtil.isNotFullyTransparent(new Color(0, 0, 0, 1))); + } } 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 1156557160..a5d64b6a43 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 @@ -35,6 +35,7 @@ import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.util.Arrays; +import java.util.function.Predicate; import javax.annotation.Nonnull; import org.apache.commons.lang3.ArrayUtils; import static org.junit.Assert.assertEquals; @@ -193,6 +194,22 @@ public class ImageUtilTest } } + @Test + public void resizeCanvas() + { + assert(bufferedImagesEqual(centeredPixel(BLACK), ImageUtil.resizeCanvas(oneByOne(BLACK), 3, 3))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.resizeCanvas(oneByOne(BLACK), 1, 1))); + assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.resizeCanvas(centeredPixel(BLACK), 1, 1))); + + BufferedImage expected = new BufferedImage(2, 1, BufferedImage.TYPE_INT_ARGB); + expected.setRGB(1, 0, BLACK.getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.resizeCanvas(oneByOne(BLACK), 2, 1))); + + expected = new BufferedImage(1, 2, BufferedImage.TYPE_INT_ARGB); + expected.setRGB(0, 1, BLACK.getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.resizeCanvas(oneByOne(BLACK), 1, 2))); + } + @Test public void rotateImage() { @@ -230,6 +247,79 @@ public class ImageUtilTest assert(bufferedImagesEqual(BLACK_PIXEL_BOTTOM_RIGHT, ImageUtil.flipImage(BLACK_PIXEL_TOP_LEFT, true, true))); } + @Test + public void fillImage() + { + // fillImage(BufferedImage image, Color color) + assert(bufferedImagesEqual(centeredPixel(GRAY), ImageUtil.fillImage(centeredPixel(BLACK), GRAY))); + assert(bufferedImagesEqual(solidColor(3, 3, GREEN), ImageUtil.fillImage(solidColor(3, 3, BLACK), GREEN))); + assert(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.fillImage(oneByOne(BLACK_TRANSPARENT), WHITE))); + + // fillImage(BufferedImage image, Color color, Predicate fillCondition) + BufferedImage expected = solidColor(CORNER_SIZE, CORNER_SIZE, WHITE); + expected.setRGB(0, 0, new Color(0, true).getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.fillImage(BLACK_PIXEL_TOP_LEFT, WHITE, ColorUtil::isFullyTransparent))); + } + + @Test + public void outlineImage() + { + // outlineImage(BufferedImage image, Color color) + BufferedImage expected = new BufferedImage(CENTERED_SIZE, CENTERED_SIZE, BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < expected.getWidth(); x++) + { + for (int y = 0; y < expected.getHeight(); y++) + { + if (x != 1 && y != 1) + { + continue; + } + expected.setRGB(x, y, BLACK.getRGB()); + } + } + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(centeredPixel(BLACK), BLACK))); + expected.setRGB(1, 1, WHITE.getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(centeredPixel(WHITE), BLACK))); + expected = solidColor(CORNER_SIZE, CORNER_SIZE, WHITE); + expected.setRGB(0, 0, BLACK.getRGB()); + expected.setRGB(1, 1, new Color(0, true).getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(BLACK_PIXEL_TOP_LEFT, WHITE))); + + // outlineImage(BufferedImage image, Color color, Predicate fillCondition) + BufferedImage test = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB); + test.setRGB(0, 0, BLACK.getRGB()); + test.setRGB(1, 0, GRAY.getRGB()); + expected = test; + expected.setRGB(0, 1, BLUE.getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(test, BLUE, (color -> color.equals(BLACK))))); + + // outlineImage(BufferedImage image, Color color, Boolean outlineCorners) + expected = solidColor(CORNER_SIZE, CORNER_SIZE, WHITE); + expected.setRGB(0, 0, BLACK.getRGB()); + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(BLACK_PIXEL_TOP_LEFT, WHITE, true))); + assert(bufferedImagesEqual(solidColor(3, 3, BLACK), ImageUtil.outlineImage(centeredPixel(BLACK), BLACK, true))); + + // outlineImage(BufferedImage image, Color color, Predicate fillCondition, Boolean outlineCorners) + test = new BufferedImage(5, 5, BufferedImage.TYPE_INT_ARGB); + test.setRGB(2, 2, BLACK.getRGB()); + test.setRGB(1,2, new Color(50, 50, 50).getRGB()); + test.setRGB(3, 2, new Color(100, 100, 100).getRGB()); + test.setRGB(2, 3, new Color(150, 150, 150).getRGB()); + expected = test; + expected.setRGB(2, 1, RED.getRGB()); + expected.setRGB(3, 1, RED.getRGB()); + expected.setRGB(4, 1, RED.getRGB()); + expected.setRGB(4, 2, RED.getRGB()); + expected.setRGB(1, 3, RED.getRGB()); + expected.setRGB(3, 3, RED.getRGB()); + expected.setRGB(4, 3, RED.getRGB()); + expected.setRGB(1, 4, RED.getRGB()); + expected.setRGB(2, 4, RED.getRGB()); + expected.setRGB(3, 4, RED.getRGB()); + Predicate testPredicate = (color -> ColorUtil.isNotFullyTransparent(color) && color.getRed() > 75 && color.getGreen() > 75 && color.getBlue() > 75); + assert(bufferedImagesEqual(expected, ImageUtil.outlineImage(test, RED, testPredicate, true))); + } + /** * Compares whether two {@link BufferedImage}s are equal in data. *