Add image utility class
This commit is contained in:
@@ -89,7 +89,7 @@ import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.ComboBoxListRenderer;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@Slf4j
|
||||
public class ConfigPanel extends PluginPanel
|
||||
@@ -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(SwingUtil.grayscaleOffset(backIcon, -100));
|
||||
BACK_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(backIcon, -100));
|
||||
SEARCH = new ImageIcon(ImageIO.read(IconTextField.class.getResourceAsStream("search.png")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import org.apache.commons.text.similarity.JaroWinklerDistance;
|
||||
|
||||
class PluginListItem extends JPanel
|
||||
@@ -98,7 +98,7 @@ class PluginListItem extends JPanel
|
||||
OFF_STAR = new ImageIcon(ImageIO.read(ConfigPanel.class.getResourceAsStream("stars/off.png")));
|
||||
}
|
||||
|
||||
CONFIG_ICON_HOVER = new ImageIcon(SwingUtil.grayscaleOffset(configIcon, -100));
|
||||
CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(configIcon, -100));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
||||
@@ -52,7 +52,7 @@ import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@Singleton
|
||||
class KourendLibraryPanel extends PluginPanel
|
||||
@@ -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(SwingUtil.grayscaleOffset(resetIcon, -100));
|
||||
RESET_CLICK_ICON = new ImageIcon(ImageUtil.grayscaleOffset(resetIcon, -100));
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
||||
@@ -32,8 +32,6 @@ import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
@@ -60,6 +58,7 @@ import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.BackgroundComponent;
|
||||
import net.runelite.client.ui.overlay.components.TextComponent;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@Slf4j
|
||||
public class PuzzleSolverOverlay extends Overlay
|
||||
@@ -448,7 +447,7 @@ public class PuzzleSolverOverlay extends Overlay
|
||||
{
|
||||
if (upArrow == null)
|
||||
{
|
||||
upArrow = getRotatedImage(getDownArrow(), Math.PI);
|
||||
upArrow = ImageUtil.rotateImage(getDownArrow(), Math.PI);
|
||||
}
|
||||
return upArrow;
|
||||
}
|
||||
@@ -457,7 +456,7 @@ public class PuzzleSolverOverlay extends Overlay
|
||||
{
|
||||
if (leftArrow == null)
|
||||
{
|
||||
leftArrow = getRotatedImage(getDownArrow(), Math.PI / 2);
|
||||
leftArrow = ImageUtil.rotateImage(getDownArrow(), Math.PI / 2);
|
||||
}
|
||||
return leftArrow;
|
||||
}
|
||||
@@ -466,16 +465,8 @@ public class PuzzleSolverOverlay extends Overlay
|
||||
{
|
||||
if (rightArrow == null)
|
||||
{
|
||||
rightArrow = getRotatedImage(getDownArrow(), 3 * Math.PI / 2);
|
||||
rightArrow = ImageUtil.rotateImage(getDownArrow(), 3 * Math.PI / 2);
|
||||
}
|
||||
return rightArrow;
|
||||
}
|
||||
|
||||
private BufferedImage getRotatedImage(BufferedImage image, double theta)
|
||||
{
|
||||
AffineTransform transform = new AffineTransform();
|
||||
transform.rotate(theta, image.getWidth() / 2, image.getHeight() / 2);
|
||||
AffineTransformOp transformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
|
||||
return transformOp.filter(image, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,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.FlatTextField;
|
||||
import net.runelite.client.util.SwingUtil;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
class ScreenMarkerPanel extends JPanel
|
||||
{
|
||||
@@ -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(SwingUtil.grayscaleOffset(borderImg, -100));
|
||||
BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(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(SwingUtil.grayscaleOffset(noBorderImg, -100));
|
||||
NO_BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(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(SwingUtil.grayscaleOffset(fillImg, -100));
|
||||
FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(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(SwingUtil.grayscaleOffset(noFillImg, -100));
|
||||
NO_FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(noFillImg, -100));
|
||||
|
||||
BufferedImage opacityImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("opacity_icon.png"));
|
||||
FULL_OPACITY_ICON = new ImageIcon(opacityImg);
|
||||
OPACITY_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(opacityImg, -100));
|
||||
NO_OPACITY_ICON = new ImageIcon(SwingUtil.grayscaleOffset(opacityImg, -150));
|
||||
OPACITY_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(opacityImg, -100));
|
||||
NO_OPACITY_ICON = new ImageIcon(ImageUtil.grayscaleOffset(opacityImg, -150));
|
||||
|
||||
BufferedImage visibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("visible_icon.png"));
|
||||
VISIBLE_ICON = new ImageIcon(visibleImg);
|
||||
VISIBLE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(visibleImg, -100));
|
||||
VISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(visibleImg, -100));
|
||||
|
||||
BufferedImage invisibleImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("invisible_icon.png"));
|
||||
INVISIBLE_ICON = new ImageIcon(invisibleImg);
|
||||
INVISIBLE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(invisibleImg, -100));
|
||||
INVISIBLE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(invisibleImg, -100));
|
||||
|
||||
BufferedImage deleteImg = ImageIO.read(ScreenMarkerPlugin.class.getResourceAsStream("delete_icon.png"));
|
||||
DELETE_ICON = new ImageIcon(deleteImg);
|
||||
DELETE_HOVER_ICON = new ImageIcon(SwingUtil.grayscaleOffset(deleteImg, -100));
|
||||
DELETE_HOVER_ICON = new ImageIcon(ImageUtil.grayscaleOffset(deleteImg, -100));
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Jordan Atwood <jordan.atwood423@gmail.com>
|
||||
* 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 OWNER 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.util;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RescaleOp;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Various Image/BufferedImage utilities.
|
||||
*/
|
||||
public class ImageUtil
|
||||
{
|
||||
/**
|
||||
* Offsets an image in the grayscale (darkens/brightens) by a given offset.
|
||||
*
|
||||
* @param image The image to be darkened or brightened.
|
||||
* @param offset A signed 8-bit integer value to brighten or darken the image with.
|
||||
* Values above 0 will brighten, and values below 0 will darken.
|
||||
* @return The given image with its brightness adjusted by the given offset.
|
||||
*/
|
||||
public static BufferedImage grayscaleOffset(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);
|
||||
for (int i = 0; i < numComponents; i++)
|
||||
{
|
||||
offsets[i] = offsetFloat;
|
||||
}
|
||||
// Set alpha to not offset
|
||||
offsets[numComponents - 1] = 0f;
|
||||
|
||||
return offset(image, scales, offsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-size a BufferedImage to the given dimensions.
|
||||
*
|
||||
* @param image the BufferedImage.
|
||||
* @param newWidth The width to set the BufferedImage to.
|
||||
* @param newHeight The height to set the BufferedImage to.
|
||||
* @return The BufferedImage with the specified dimensions
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates an image around its center by a given number of radians.
|
||||
*
|
||||
* @param image The image to be rotated.
|
||||
* @param theta The number of radians to rotate the image.
|
||||
* @return The given image, rotated by the given theta.
|
||||
*/
|
||||
public static BufferedImage rotateImage(final BufferedImage image, final double theta)
|
||||
{
|
||||
AffineTransform transform = new AffineTransform();
|
||||
transform.rotate(theta, image.getWidth() / 2.0, image.getHeight() / 2.0);
|
||||
AffineTransformOp transformOp = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
|
||||
return transformOp.filter(image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rescale operation on the image's color components.
|
||||
*
|
||||
* @param image The image to be adjusted.
|
||||
* @param scales An array of scale operations to be performed on the image's color components.
|
||||
* @param offsets An array of offset operations to be performed on the image's color components.
|
||||
* @return The modified image after applying the given adjustments.
|
||||
*/
|
||||
private static BufferedImage offset(final BufferedImage image, final float[] scales, final float[] offsets)
|
||||
{
|
||||
return new RescaleOp(scales, offsets, null).filter(image, null);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.SystemTray;
|
||||
@@ -41,8 +40,6 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RescaleOp;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -105,27 +102,6 @@ public class SwingUtil
|
||||
System.setProperty("sun.awt.noerasebackground", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets an image in the grayscale (darkens/brightens) by an offset
|
||||
*/
|
||||
public static BufferedImage grayscaleOffset(BufferedImage image, 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);
|
||||
for (int i = 0; i < numComponents; i++)
|
||||
{
|
||||
offsets[i] = offsetFloat;
|
||||
}
|
||||
// Set alpha to not offset
|
||||
offsets[numComponents - 1] = 0f;
|
||||
|
||||
return offset(image, scales, offsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely sets Swing theme
|
||||
*
|
||||
@@ -266,25 +242,6 @@ public class SwingUtil
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-size a BufferedImage to the given dimensions.
|
||||
*
|
||||
* @param image the BufferedImage.
|
||||
* @param newWidth The width to set the BufferedImage to.
|
||||
* @param newHeight The height to set the BufferedImage to.
|
||||
* @return The BufferedImage with the specified dimensions
|
||||
*/
|
||||
private static BufferedImage resizeImage(BufferedImage image, int newWidth, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create swing button from navigation button.
|
||||
*
|
||||
@@ -300,7 +257,7 @@ public class SwingUtil
|
||||
{
|
||||
|
||||
final BufferedImage scaledImage = iconSize > 0
|
||||
? resizeImage(navigationButton.getIcon(), iconSize, iconSize)
|
||||
? ImageUtil.resizeImage(navigationButton.getIcon(), iconSize, iconSize)
|
||||
: navigationButton.getIcon();
|
||||
|
||||
final JButton button = new JButton();
|
||||
@@ -350,17 +307,4 @@ public class SwingUtil
|
||||
{
|
||||
return SubstanceCoreUtilities.getTitlePaneComponent(frame) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rescale operation on the image's color components.
|
||||
*
|
||||
* @param image The image to be adjusted.
|
||||
* @param scales An array of scale operations to be performed on the image's color components.
|
||||
* @param offsets An array of offset operations to be performed on the image's color components.
|
||||
* @return The modified image after applying the given adjustments.
|
||||
*/
|
||||
private static BufferedImage offset(final BufferedImage image, final float[] scales, final float[] offsets)
|
||||
{
|
||||
return new RescaleOp(scales, offsets, null).filter(image, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Jordan Atwood <jordan.atwood423@gmail.com>
|
||||
* 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 OWNER 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.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import static java.awt.Color.BLACK;
|
||||
import static java.awt.Color.BLUE;
|
||||
import static java.awt.Color.GRAY;
|
||||
import static java.awt.Color.GREEN;
|
||||
import static java.awt.Color.RED;
|
||||
import static java.awt.Color.WHITE;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ImageUtilTest
|
||||
{
|
||||
private static final Color BLACK_HALF_TRANSPARENT = new Color(0, 0, 0, 128);
|
||||
private static final Color BLACK_TRANSPARENT = new Color(0, true);
|
||||
private static final int CORNER_SIZE = 2;
|
||||
private static final int CENTERED_SIZE = 3;
|
||||
|
||||
private static final BufferedImage BLACK_PIXEL_TOP_LEFT;
|
||||
private static final BufferedImage BLACK_PIXEL_TOP_RIGHT;
|
||||
private static final BufferedImage BLACK_PIXEL_BOTTOM_LEFT;
|
||||
private static final BufferedImage BLACK_PIXEL_BOTTOM_RIGHT;
|
||||
|
||||
static
|
||||
{
|
||||
BLACK_PIXEL_TOP_LEFT = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
BLACK_PIXEL_TOP_LEFT.setRGB(0, 0, BLACK.getRGB());
|
||||
|
||||
BLACK_PIXEL_TOP_RIGHT = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
BLACK_PIXEL_TOP_RIGHT.setRGB(1, 0, BLACK.getRGB());
|
||||
|
||||
BLACK_PIXEL_BOTTOM_LEFT = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
BLACK_PIXEL_BOTTOM_LEFT.setRGB(0, 1, BLACK.getRGB());
|
||||
|
||||
BLACK_PIXEL_BOTTOM_RIGHT = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
BLACK_PIXEL_BOTTOM_RIGHT.setRGB(1, 1, BLACK.getRGB());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void grayscaleOffset()
|
||||
{
|
||||
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)));
|
||||
assert(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(GRAY), -255)));
|
||||
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)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resizeImage()
|
||||
{
|
||||
// TODO: test image contents after changing size
|
||||
|
||||
final BufferedImage larger = ImageUtil.resizeImage(oneByOne(BLACK), 46, 46);
|
||||
final BufferedImage smaller = ImageUtil.resizeImage(centeredPixel(WHITE), 1, 1);
|
||||
final BufferedImage stretched = ImageUtil.resizeImage(solidColor(30, 30, RED), 12, 34);
|
||||
|
||||
assertEquals(46, larger.getWidth());
|
||||
assertEquals(46, larger.getHeight());
|
||||
assertEquals(1, smaller.getWidth());
|
||||
assertEquals(1, smaller.getHeight());
|
||||
assertEquals(12, stretched.getWidth());
|
||||
assertEquals(34, stretched.getHeight());
|
||||
|
||||
final BufferedImage[] assertSameAfterResize = new BufferedImage[] {
|
||||
oneByOne(WHITE),
|
||||
oneByOne(GRAY),
|
||||
oneByOne(BLACK),
|
||||
oneByOne(RED),
|
||||
oneByOne(GREEN),
|
||||
oneByOne(BLUE),
|
||||
oneByOne(BLACK_HALF_TRANSPARENT),
|
||||
oneByOne(BLACK_TRANSPARENT),
|
||||
centeredPixel(WHITE),
|
||||
centeredPixel(GRAY),
|
||||
centeredPixel(BLACK),
|
||||
BLACK_PIXEL_TOP_LEFT,
|
||||
BLACK_PIXEL_TOP_RIGHT,
|
||||
BLACK_PIXEL_BOTTOM_LEFT,
|
||||
BLACK_PIXEL_BOTTOM_RIGHT,
|
||||
};
|
||||
for (BufferedImage image : assertSameAfterResize)
|
||||
{
|
||||
assert(bufferedImagesEqual(image, ImageUtil.resizeImage(image, image.getWidth(), image.getHeight())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rotateImage()
|
||||
{
|
||||
// TODO: Test more than 90° rotations
|
||||
|
||||
// Evenly-sized images (2x2)
|
||||
assert(bufferedImagesEqual(BLACK_PIXEL_TOP_RIGHT, ImageUtil.rotateImage(BLACK_PIXEL_TOP_LEFT, Math.PI / 2)));
|
||||
assert(bufferedImagesEqual(BLACK_PIXEL_BOTTOM_RIGHT, ImageUtil.rotateImage(BLACK_PIXEL_TOP_LEFT, Math.PI)));
|
||||
assert(bufferedImagesEqual(BLACK_PIXEL_BOTTOM_LEFT, ImageUtil.rotateImage(BLACK_PIXEL_TOP_LEFT, Math.PI * 3 / 2)));
|
||||
assert(bufferedImagesEqual(BLACK_PIXEL_TOP_LEFT, ImageUtil.rotateImage(BLACK_PIXEL_TOP_LEFT, Math.PI * 2)));
|
||||
|
||||
// Unevenly-sized images (2x1); when rotated 90° become (2x2) images
|
||||
final BufferedImage twoByOneLeft = new BufferedImage(2, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
twoByOneLeft.setRGB(0, 0, BLACK.getRGB());
|
||||
final BufferedImage twoByTwoRight = new BufferedImage(2, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
twoByTwoRight.setRGB(1, 0, BLACK.getRGB());
|
||||
final BufferedImage oneByTwoTop = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
|
||||
oneByTwoTop.setRGB(1, 0, new Color(0, 0, 0, 127).getRGB());
|
||||
final BufferedImage oneByTwoBottom = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
|
||||
oneByTwoBottom.setRGB(0, 0, new Color(0, 0, 0, 127).getRGB());
|
||||
oneByTwoBottom.setRGB(0, 1, BLACK.getRGB());
|
||||
|
||||
assert(bufferedImagesEqual(oneByTwoTop, ImageUtil.rotateImage(twoByOneLeft, Math.PI / 2)));
|
||||
assert(bufferedImagesEqual(twoByTwoRight, ImageUtil.rotateImage(twoByOneLeft, Math.PI)));
|
||||
assert(bufferedImagesEqual(oneByTwoBottom, ImageUtil.rotateImage(twoByOneLeft, Math.PI * 3 / 2)));
|
||||
assert(bufferedImagesEqual(twoByOneLeft, ImageUtil.rotateImage(twoByOneLeft, Math.PI * 2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares whether two {@link BufferedImage}s are equal in data.
|
||||
*
|
||||
* @param expected The first {@link BufferedImage} to be compared.
|
||||
* @param actual The second {@link BufferedImage} to be compared.
|
||||
* @return A boolean indicating whether the given {@link BufferedImage}s are of the same image data.
|
||||
*/
|
||||
private boolean bufferedImagesEqual(final @Nonnull BufferedImage expected, final @Nonnull BufferedImage actual)
|
||||
{
|
||||
if (expected.getWidth() != actual.getWidth())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!expected.getColorModel().equals(actual.getColorModel()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final DataBuffer aBuffer = expected.getRaster().getDataBuffer();
|
||||
final DataBuffer bBuffer = actual.getRaster().getDataBuffer();
|
||||
final DataBufferInt aBufferInt = (DataBufferInt) aBuffer;
|
||||
final DataBufferInt bBufferInt = (DataBufferInt) bBuffer;
|
||||
|
||||
if (aBufferInt.getNumBanks() != bBufferInt.getNumBanks())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < aBufferInt.getNumBanks(); i++)
|
||||
{
|
||||
final int[] aDataBank = aBufferInt.getData(i);
|
||||
final int[] bDataBank = bBufferInt.getData(i);
|
||||
if (!Arrays.equals(aDataBank, bDataBank))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BufferedImage} of a 1-by-1px image of the given color.
|
||||
*
|
||||
* @param color The color to use for the image's single pixel.
|
||||
* @return A {@link BufferedImage} containing a single pixel of the given color.
|
||||
*/
|
||||
private BufferedImage oneByOne(final @Nonnull Color color)
|
||||
{
|
||||
return solidColor(1, 1, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BufferedImage} of a single pixel of the given color centered in a 3-by-3px
|
||||
* image.
|
||||
*
|
||||
* @param color The color to use for the centered pixel.
|
||||
* @return A {@link BufferedImage} with completely transparent pixels and one pixel of the
|
||||
* given color in the center.
|
||||
*/
|
||||
private BufferedImage centeredPixel(final @Nonnull Color color)
|
||||
{
|
||||
final BufferedImage out = new BufferedImage(CENTERED_SIZE, CENTERED_SIZE, BufferedImage.TYPE_INT_ARGB);
|
||||
out.setRGB(1, 1, color.getRGB());
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BufferedImage} of a solid color of given width and height.
|
||||
*
|
||||
* @param width The desired width of the color image.
|
||||
* @param height The desired height of the color image.
|
||||
* @param color The desired color of the image.
|
||||
* @return A {@link BufferedImage} of given dimensions filled with the given color.
|
||||
*/
|
||||
private BufferedImage solidColor(final int width, final int height, final @Nonnull Color color)
|
||||
{
|
||||
final BufferedImage out = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
out.setRGB(x, y, color.getRGB());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user