Java: поддержание пропорций фонового изображения JPanel
У меня есть JPanel
с нарисованным фоновым изображением и менеджером макета, содержащим другие меньшие изображения, все это внутри JFrame
, Фоновое изображение довольно большое, и я хочу, чтобы оно сохраняло свое соотношение сторон на большом или маленьком мониторе.
В конце концов, я хочу иметь возможность LayoutManager
и меньшие изображения в его ячейках "приклеены" к фоновой картинке.
Я искал ресурсы, и кажется, что многие примеры используют BufferedImage
но я нет; это создаст проблему? Я опубликую свой код ниже для рисования изображения. Если мне не хватает информации, пожалуйста, дайте мне знать.
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);
}
}
РЕДАКТИРОВАТЬ: я должен упомянуть, что я знаю формулу соотношения сторон: оригинальная высота / оригинальная ширина х новая ширина = новая высота Однако я не знаю, как правильно использовать это в моих интересах.
4 ответа
Ну, самым быстрым и простым решением является использование Image.getScaledInstance
g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);
Если вы задаетесь вопросом об отрицательном числе, документы по Java говорят:
Если ширина или высота является отрицательным числом, то подставляется значение, чтобы сохранить соотношение сторон исходных размеров изображения. Если ширина и высота отрицательны, то используются исходные размеры изображения.
ОБНОВИТЬ
Просто как примечание (мой Google играл).
getScaledInstance
Это не самый быстрый и не самый качественный подход, но он самый простой.
Прочитайте "Опасности Image.getScaledInstance", чтобы узнать больше идей
ОБНОВИТЬ
Масштабирование изображения по размеру области немного сложнее, чем просто масштабирование соотношения сторон. Вы должны сделать выбор, если хотите, чтобы изображение "вписывалось" в область (возможно, оставляя пустые области вокруг него), или поверх "заливки" области (чтобы его наименьшее измерение соответствовало наибольшему измерению области).
Fit & Fill
В основном я работаю с масштабными коэффициентами
Это возвращает коэффициент масштабирования для определенного размера. Я использую это, чтобы принять решение о том, какой фактор я хочу использовать, основываясь на том, какой алгоритм мне нужен
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;
}
Используется этими двумя методами. Они просто берут два Dimension
s. Оригинал и цель.
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;
}
Относительно просто передать изображение (напрямую или через метод поддержки). Так, например, вы могли бы назвать это изнутри вашего paint
метод
double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());
int scaledWidth = image.getWidth() * scale;
int scaledHeight *= image.getWidth() * scale;
Это автоматически позаботится о соотношении сторон для вас;)
ОБНОВЛЕНО с расширенным примером
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);
}
Попробуйте что-то вроде этого:
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SG2B2 {
JFrame frame;
public static void main(String[] args) {
SG2B2 gui = new SG2B2();
gui.createUI();
}
public void createUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
frame.setSize(300, 400);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
Image image;
private final String pic = "Logo.jpg";
public MyDrawPanel() {
image = new ImageIcon(pic).getImage();
image = scaleImage(image);
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, this);
}
private Image scaleImage(Image rawImage) {
Image scaledImage = null;
System.out.println("Scaling");
try {
int rawImageWidth = rawImage.getWidth(this);
int rawImageHeight = rawImage.getHeight(this);
int paneWidth = (int) getWidth();
int paneHeight = (int) getHeight();
System.out.println("Image W = " + rawImageWidth
+ ", H = " + rawImageHeight
+ "; Pane W = " + paneWidth
+ ", H = " + paneHeight);
// preserve the original ratio
float widthRatio = (float) rawImageWidth / (float) paneWidth;
float heightRatio = (float) rawImageHeight / (float) paneHeight;
int widthFactor = -1;
int heightFactor = -1;
if ((widthRatio > heightRatio) && (widthRatio > 1.0)) {
widthFactor = paneWidth;
} else if ((heightRatio > widthRatio) && (heightRatio > 1.0)) {
heightFactor = paneHeight;
}
System.out.println("widthRatio = "
+ String.format("%.3f", widthRatio)
+ ", heightRatio = "
+ String.format("%.3f", heightRatio));
System.out.println("widthFactor = " + widthFactor
+ ", heightFactor = " + heightFactor);
if ((widthFactor < 0) && (heightFactor < 0)) {
scaledImage = rawImage;
} else {
scaledImage = rawImage.getScaledInstance(widthFactor, heightFactor,
Image.SCALE_SMOOTH);
// load the new image, 'getScaledInstance' loads asynchronously
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(scaledImage, 0);
tracker.waitForID(0);
}
} catch (InterruptedException ie) {
System.err.println("load interrupt: " + ie.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return (scaledImage);
}
}
}
который в конечном итоге будет масштабировать изображение до JPanel
размер с помощью getScaledInstance(int width, int height, ImageObserver io)
Для всех, кто интересуется внесением изменений в метод PaintComponent MadProgrammer, можно значительно ускорить обновление экрана.
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) 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 - scaleWidth) / 2;
int y = (height - scaleHeight) / 2;
g2d.drawImage(image, x, y, scaleWidth, scaleHeight, this);
}
Я придумал это решение:
public class ImageLabel extends JPanel {
private Image image = null;
public void setImage(Image img) {
image = img;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
int imgWidth, imgHeight;
double contRatio = (double) getWidth() / (double) getHeight();
double imgRatio = (double) image.getWidth(this) / (double) image.getHeight(this);
//width limited
if(contRatio < imgRatio){
imgWidth = getWidth();
imgHeight = (int) (getWidth() / imgRatio);
//height limited
}else{
imgWidth = (int) (getHeight() * imgRatio);
imgHeight = getHeight();
}
//to center
int x = (int) (((double) getWidth() / 2) - ((double) imgWidth / 2));
int y = (int) (((double) getHeight()/ 2) - ((double) imgHeight / 2));
g.drawImage(image, x, y, imgWidth, imgHeight, this);
}
}
}