package magic.ui.helpers;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsDevice.WindowTranslucency;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import magic.data.MagicIcon;
import magic.ui.MagicImages;
import magic.ui.image.filter.ColorSwapImageFilter;
import magic.ui.image.filter.GrayScaleImageFilter;
import magic.ui.image.filter.WhiteColorSwapImageFilter;
import magic.ui.theme.Theme;
import magic.ui.utility.MagicStyle;
import magic.utility.MagicFileSystem;
import magic.utility.MagicFileSystem.DataPath;
final public class ImageHelper {
private static final GrayScaleImageFilter GRAYSCALE_FILTER = new GrayScaleImageFilter();
private final static GraphicsConfiguration GC = (GraphicsEnvironment.isHeadless() == false) ?
GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration() :
null;
private ImageHelper() {}
public static BufferedImage scale(final BufferedImage img, final int targetWidth, final int targetHeight) {
if (img.getWidth() == targetWidth && img.getHeight() == targetHeight) {
return img;
} else {
return scale(
img,
targetWidth,
targetHeight,
RenderingHints.VALUE_INTERPOLATION_BILINEAR,
targetWidth < img.getWidth()
);
}
}
/**
* Convenience method that returns a scaled instance of the
* provided {@code BufferedImage}.
*
* @param img the original image to be scaled
* @param targetWidth the desired width of the scaled instance,
* in pixels
* @param targetHeight the desired height of the scaled instance,
* in pixels
* @param hint one of the rendering hints that corresponds to
* {@code RenderingHints.KEY_INTERPOLATION} (e.g.
* {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
* {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
* {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
* @param higherQuality if true, this method will use a multi-step
* scaling technique that provides higher quality than the usual
* one-step technique (only useful in downscaling cases, where
* {@code targetWidth} or {@code targetHeight} is
* smaller than the original dimensions, and generally only when
* the {@code BILINEAR} hint is specified)
* @return a scaled version of the original {@code BufferedImage}
*/
public static BufferedImage scale(
final BufferedImage img,
final int targetWidth,
final int targetHeight,
final Object hint,
final boolean higherQuality
) {
BufferedImage ret = img;
int w;
int h;
if (higherQuality && (img.getWidth() > targetWidth && img.getHeight() > targetHeight)) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
final BufferedImage tmp = getCompatibleBufferedImage(w, h, img.getTransparency());
final Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
public static File doScreenshotToFile(final Component container, final Path filePath, final String imageType) throws IOException {
final File imageFile = new File(filePath.toString());
ImageIO.write(getScreenshotImage(container), imageType, imageFile);
return imageFile;
}
/**
* Creates an image of the contents of container and saves to file.
*/
public static File doScreenshotToFile(final Component container, final Path filePath) throws IOException {
return doScreenshotToFile(container, filePath, "png");
}
private static BufferedImage getScreenshotImage(final Component container) {
final Rectangle rec = container.getBounds();
final BufferedImage capture = getCompatibleBufferedImage(rec.width, rec.height);
container.paint(capture.getGraphics());
return capture;
}
public static BufferedImage getCompatibleBufferedImage(final int width, final int height, final int transparency) {
if (GC != null) {
return GC.createCompatibleImage(width, height, transparency);
} else {
final int type = (transparency == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
return new BufferedImage(width, height, type);
}
}
public static BufferedImage getCompatibleBufferedImage(final int width, final int height) {
return getCompatibleBufferedImage(width, height, Transparency.OPAQUE);
}
public static BufferedImage getCompatibleBufferedImage(BufferedImage image) {
return getCompatibleBufferedImage(image.getWidth(), image.getHeight(), image.getTransparency());
}
public static BufferedImage getBufferedImage(ImageIcon icon) {
BufferedImage bi = getCompatibleBufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TRANSLUCENT);
Graphics g = bi.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
return bi;
}
public static boolean isValidImageFile(final Path imageFilePath) {
try {
final BufferedImage image = ImageIO.read(imageFilePath.toFile());
return (image != null);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static boolean isValidImageFile(File aFile) {
return isValidImageFile(aFile.toPath());
}
/**
* The images returned by ImageIO are often in custom formats which can draw really,
* really slowly. For best performance, it's often best to draw any image returned by
* ImageIO into a new image of the appropriate pixel format for your system.
* (http://www.jhlabs.com/ip/managed_images.html)
*/
public static BufferedImage getOptimizedImage(final BufferedImage source) {
final BufferedImage buffImage = getCompatibleBufferedImage(
source.getWidth(),
source.getHeight(),
source.getTransparency()
);
buffImage.getGraphics().drawImage(source, 0, 0, null);
return buffImage;
}
public static BufferedImage getCustomBackgroundImage() {
final File file = MagicFileSystem.getDataPath(DataPath.MODS).resolve("background.image").toFile();
BufferedImage image = null;
try {
image = ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
}
return (image != null) ?
ImageHelper.getOptimizedImage(image) :
MagicStyle.getTheme().getTexture(Theme.TEXTURE_BACKGROUND);
}
public static boolean isWindowTranslucencySupported() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
return gd.isWindowTranslucencySupported(WindowTranslucency.TRANSLUCENT);
}
private static final Paint debugBorderPaint = new GradientPaint(0, 0, Color.red, 100, 100, Color.white, true);
public static void setDebugBorder(final JComponent component) {
component.setBorder(BorderFactory.createDashedBorder(debugBorderPaint));
}
public static BufferedImage getConvertedIcon(final ImageIcon icon) {
final BufferedImage bi =
ImageHelper.getCompatibleBufferedImage(
icon.getIconWidth(), icon.getIconHeight(), Transparency.TRANSLUCENT);
final Graphics g = bi.createGraphics();
// paint the Icon to the BufferedImage.
icon.paintIcon(null, g, 0, 0);
g.dispose();
return bi;
}
public static void drawStringWithOutline(final Graphics g, final String str, int x, int y, Color fillColor, Color outlineColor) {
g.setColor(outlineColor);
for (int i = 1; i <= 1; i++) {
g.drawString(str, x+i, y);
g.drawString(str, x-i, y);
g.drawString(str, x, y+i);
g.drawString(str, x, y-i);
}
g.setColor(fillColor);
g.drawString(str,x,y);
}
public static void drawStringWithOutline(final Graphics g, final String str, int x, int y) {
drawStringWithOutline(g, str, x, y, Color.WHITE, Color.BLACK);
}
public static void clearImage(final BufferedImage image) {
final Graphics2D g2d = image.createGraphics();
final Composite composite = g2d.getComposite();
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
g2d.setComposite(composite);
g2d.dispose();
}
public static Icon getRecoloredIcon(ImageIcon icon, Color oldColor, Color newColor) {
final FilteredImageSource fis = new FilteredImageSource(
icon.getImage().getSource(),
new ColorSwapImageFilter(oldColor, newColor)
);
return new ImageIcon(Toolkit.getDefaultToolkit().createImage(fis));
}
public static Icon getRecoloredIcon(MagicIcon icon, Color oldColor, Color newColor) {
return getRecoloredIcon(MagicImages.getIcon(icon), oldColor, newColor);
}
/**
* Converts a white icon to the specified color.
*
* Recommend use {@link #getRecoloredIcon(ImageIcon, Color, Color) } instead.
*/
public static Icon getRecoloredIcon(ImageIcon icon, Color newColor) {
return getRecoloredIcon(icon, Color.WHITE, newColor);
}
/**
* Given a WHITE image, converts to given color.
*/
public static Image getColoredImage(final Image aImage, final Color newColor) {
final FilteredImageSource fis = new FilteredImageSource(
aImage.getSource(),
new WhiteColorSwapImageFilter(newColor)
);
return Toolkit.getDefaultToolkit().createImage(fis);
}
public static Image getGreyScaleImage(final Image colorImage) {
final FilteredImageSource fis = new FilteredImageSource(
colorImage.getSource(),
GRAYSCALE_FILTER
);
return Toolkit.getDefaultToolkit().createImage(fis);
}
public static BufferedImage getTranslucentImage(BufferedImage image, float opacity) {
final BufferedImage newImage = ImageHelper.getCompatibleBufferedImage(image);
final Graphics2D g2d = newImage.createGraphics();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return newImage;
}
public static Image getTranslucentImage(Image image, float opacity) {
final BufferedImage newImage = getCompatibleBufferedImage(
image.getWidth(null), image.getHeight(null), Transparency.TRANSLUCENT
);
final Graphics2D g2d = newImage.createGraphics();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return newImage;
}
/**
* Returns an optimized subimage defined by a specified rectangular region.
* <p>
* getSubImage() on its own causes image to become unaccelerated.
* (see <a href="http://www.jhlabs.com/ip/managed_images.html">external link</a>)
*/
public static BufferedImage getOptimizedSubimage(BufferedImage image, Rectangle rect) {
return ImageHelper.getOptimizedImage(image.getSubimage(rect.x, rect.y, rect.width, rect.height));
}
}