Skip to content

Instantly share code, notes, and snippets.

@RustyKnight
Created June 9, 2022 00:59
Show Gist options
  • Save RustyKnight/03d7ee521803359a7653dedfef372ef7 to your computer and use it in GitHub Desktop.
Save RustyKnight/03d7ee521803359a7653dedfef372ef7 to your computer and use it in GitHub Desktop.
Example of a scalable background image in Swing
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ScalableBackground {
public static void main(String[] args) {
new ScalableBackground();
}
public ScalableBackground() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
BufferedImage img = ImageIO.read(getClass().getResource("/images/Mando01.jpeg"));
JFrame frame = new JFrame();
frame.add(new BackgroundPane(img));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(ScalableBackground.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage masterImage;
private BufferedImage scaledImage;
private Timer debounce;
public BackgroundPane(BufferedImage image) {
masterImage = image;
scaledImage = masterImage;
debounce = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
scaleBackgroundToFit();
}
});
debounce.setRepeats(false);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
debounce.start();
}
});
}
@Override
public Dimension getPreferredSize() {
BufferedImage image = scaledImage == null ? masterImage : scaledImage;
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
protected void scaleBackgroundToFit() {
scaledImage = ImageUtil.getScaledInstanceToFill(masterImage, getSize());
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (scaledImage != null) {
int xPos = (getWidth() - scaledImage.getWidth()) / 2;
int yPos = (getHeight() - scaledImage.getHeight()) / 2;
g.drawImage(scaledImage, xPos, yPos, this);
}
}
}
public class ImageUtil {
public static BufferedImage getScaledInstanceToFit(BufferedImage source, Dimension destination) {
Dimension original = new Dimension(source.getWidth(), source.getHeight());
double scaleFactor = getScaleFactorToFit(original, destination);
return getScaledInstance(source, scaleFactor);
}
public static BufferedImage getScaledInstanceToFill(BufferedImage source, Dimension destination) {
Dimension original = new Dimension(source.getWidth(), source.getHeight());
double scaleFactor = getScaleFactorToFill(original, destination);
return getScaledInstance(source, scaleFactor);
}
//
// protected static Dimension getSizeToFit(Dimension original, Dimension toFit) {
// double factor = getScaleFactorToFit(original, toFit);
// Dimension size = new Dimension(original);
// size.width *= factor;
// size.height *= factor;
// return size;
// }
//
// protected static Dimension getSizeToFill(Dimension original, Dimension toFit) {
// double factor = getScaleFactorToFill(original, toFit);
// Dimension size = new Dimension(original);
// size.width *= factor;
// size.height *= factor;
// return size;
// }
protected static double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
protected static double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
protected static double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
return Math.max(dScaleHeight, dScaleWidth);
}
protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight);
}
return imgScale;
}
protected static void applyQualityRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
protected static BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
int w = img.getWidth();
int h = img.getHeight();
do {
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected static BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w = img.getWidth();
int h = img.getHeight();
do {
if (w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment