java swing jpanel background-image aspect-ratio

Java: mantenimiento de la relación de aspecto de la imagen de fondo de JPanel



swing background-image (4)

Bueno, la solución más rápida y fácil es usar Image.getScaledInstance

g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);

Si te preguntas sobre el número negativo, los doctores de Java dicen:

Si el ancho o el alto es un número negativo, se sustituye un valor para mantener la relación de aspecto de las dimensiones de la imagen original. Si tanto el ancho como la altura son negativos, se utilizan las dimensiones de la imagen original.

ACTUALIZAR

Solo como una nota al margen (mi Google estaba jugando).

getScaledInstance no es el enfoque más rápido o de mayor calidad, pero es el más fácil.

Lea los peligros de Image.getScaledInstance para obtener más ideas

ACTUALIZAR

Escalar una imagen para que se ajuste a un área es un poco más complicada que simplemente escalar la relación de aspecto. Tiene que elegir si desea que la imagen "encaje" dentro del área (posiblemente dejando áreas en blanco a su alrededor) o sobre "llenar" el área (de modo que su dimensión más pequeña se ajuste a la dimensión más grande del área).

Ajustar y llenar

Básicamente, trabajo con factores de escala

Esto devuelve el factor de escala para un tamaño particular. Lo uso para tomar decisiones sobre qué factor quiero usar según el algoritmo que necesito

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; }

Es usado por estos dos métodos. Simplemente toman dos Dimension s. El original y el objetivo.

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; }

Es relativamente sencillo pasar una imagen a (directamente o mediante un método de soporte). Entonces, por ejemplo, puedes llamar esto desde tu método de paint

double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()); int scaledWidth = image.getWidth() * scale; int scaledHeight *= image.getWidth() * scale;

Esto se ocupará automáticamente de la relación de aspecto para usted;)

ACTUALIZADO con un ejemplo expandido

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); }

Tengo un JPanel con una imagen de fondo pintada y un administrador de disposición que contiene otras imágenes más pequeñas, todo esto dentro de un JFrame . La imagen de fondo es bastante grande y quiero que mantenga su relación de aspecto ya sea en un monitor grande o pequeño.

Eventualmente, quiero poder tener mi LayoutManager y las imágenes más pequeñas en sus celdas "pegadas" a la imagen de fondo.

Busqué recursos y parece que muchos ejemplos usan una BufferedImage pero yo no; ¿Esto representará un problema? Publicaré mi código a continuación para pintar la imagen. Si no tengo información, házmelo saber.

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); } }

EDITAR: Debo mencionar que conozco la fórmula de relación de aspecto: altura original / ancho original x ancho nuevo = altura nueva Sin embargo, no sé cómo usarlo correctamente para mi ventaja.


Para cualquier persona interesada, el método PaintComponent de MadProgrammer de la siguiente manera permite una actualización de pantalla mucho más rápida.

@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); }


Pruebe algo como esto:

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); } } }

que finalmente escalará la imagen al tamaño de getScaledInstance(int width, int height, ImageObserver io) mediante el uso de getScaledInstance(int width, int height, ImageObserver io)


Se me ocurrió esta solución:

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); } } }