I have a JPanel
with a painted background image and a layout manager holding other smaller images, all of this inside a JFrame
. The background image is pretty big and I want to be able to have it maintain its aspect ratio whether its on a big or small monitor.
Eventually, I want to be able to have my LayoutManager
and the smaller images in its cells "glued" to the background picture.
I looked around for resources and it seems that many examples use a BufferedImage
but I am not; will this pose a problem? I'll post my code below for painting the image, If I lack any information please let me know.
public class MonitorPanel extends JPanel {
Image img;
public MonitorPanel() throws MalformedURLException {
//add components
try {
img = ImageIO.read(new File("src/customer_vlans.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void paintComponent(Graphics g)
{
//paint background image
super.paintComponent(g);
//g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
g.drawImage(img, 0, 0, this);
}
}
EDIT: I should mention that I know the aspect ratio formula: original height / original width x new width = new height However, I do not know how to use that correctly to my advantage.
Well, the quickest and easiest solution is to use Image.getScaledInstance
g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);
If your wondering about the negative number, the java docs say:
If either width or height is a negative number then a value is substituted to maintain the aspect ratio of the original image dimensions. If both width and height are negative, then the original image dimensions are used.
UPDATE
Just as a side note (my Google was playing up).
getScaledInstance
is neither the fastest or highest quality approach, but it is the easiest.
Take a read through The Perils of Image.getScaledInstance for some more ideas
UPDATE
Scaling an image to fit an area is slightly more complicated then simply scaling the aspect ratio. You have to make a choice over if you want the image to "fit" within the area (possibly leaving blank areas around it) or over "fill" the area (so that it's smallest dimension fits the largest dimension of the area).
Fit & Fill
Basically, I work with scale factors
This returns the scaling factor for a particular size. I use this to make decisions about which factor I want to use based which algorithm I need
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize / (double) iMasterSize;
} else {
dScale = (double) iTargetSize / (double) iMasterSize;
}
return dScale;
}
It's used by these two methods. They simply take two Dimension
s. The original and the target.
public 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;
}
public static double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
double dScale = Math.max(dScaleHeight, dScaleWidth);
return dScale;
}
It's relatively simple to pass an image into (either directly or via a support method). So for example, you could call this from within your paint
method
double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());
int scaledWidth = image.getWidth() * scale;
int scaledHeight *= image.getWidth() * scale;
This will automatically take care of the aspect ratio for you ;)
UPDATED with expanded example
public double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize / (double) iMasterSize;
} else {
dScale = (double) iTargetSize / (double) iMasterSize;
}
return dScale;
}
public 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;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaled.getWidth(this)) / 2;
int y = (height - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
}