Java: zachowanie proporcji obrazu tła JPanel

Mam {[1] } z malowanym obrazem tła i menedżerem układu trzymającym inne mniejsze obrazy, wszystko to wewnątrz JFrame. Obraz tła jest dość duży i chcę być w stanie utrzymać jego proporcje, czy to na dużym czy małym monitorze.

W końcu chcę mieć moje LayoutManager i mniejsze obrazy w komórkach "przyklejone" do obrazu tła.

Rozejrzałem się po zasobach i wydaje się, że wiele przykładów używa BufferedImage, ale nie jestem; czy to będzie problem? Zamieszczę poniżej mój kod do malowania obrazu, jeśli brak mi jakichkolwiek informacji proszę dać mi znać.

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: powinienem wspomnieć, że znam wzór proporcji: oryginalna wysokość / oryginalna szerokość x Nowa szerokość = nowa wysokość Nie wiem jednak, jak to właściwie wykorzystać na swoją korzyść.

Author: David Kroukamp, 2012-08-14

4 answers

Cóż, najszybszym i najłatwiejszym rozwiązaniem jest użycie Image.getScaledInstance

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

Jeśli zastanawiasz się nad liczbą ujemną, dokumenty Javy mówią:

Jeśli szerokość lub wysokość jest liczbą ujemną, to wartość jest podstawione w celu zachowania proporcji oryginalnego obrazu wymiary. Jeżeli zarówno szerokość jak i wysokość są ujemne, to oryginał używane są wymiary obrazu.

UPDATE

Tak na marginesie (moje Google grało up).

getScaledInstance nie jest ani najszybsze, ani najwyższej jakości podejście, ale jest najłatwiejsze.

Przeczytaj niebezpieczeństwa obrazu.getScaledInstance więcej pomysłów

UPDATE

Skalowanie obrazu w celu dopasowania do obszaru jest nieco bardziej skomplikowane niż zwykłe skalowanie proporcji. Musisz dokonać wyboru, czy chcesz, aby obraz" zmieścił się "w obszarze (ewentualnie pozostawiając puste obszary wokół niego) lub nad" wypełnij " obszar (tak, aby najmniejszy wymiar pasuje do największego wymiaru obszaru).

FitWypełnij

Fit & Fill

Zasadniczo pracuję z czynnikami skali

Zwraca współczynnik skalowania dla określonego rozmiaru. Używam tego do podejmowania decyzji o tym, który czynnik chcę użyć na podstawie jakiego algorytmu potrzebuję

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;

}

Jest używany przez te dwie metody. Po prostu biorą dwa Dimension s. Oryginał i cel.

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;

}

Stosunkowo łatwo jest przekazać obraz do (bezpośrednio lub za pomocą metody wsparcia). Na przykład możesz wywołać to z poziomu swojej metody paint

double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());

int scaledWidth = image.getWidth() * scale;
int scaledHeight *= image.getWidth() * scale;

To automatycznie zajmie się proporcjami dla Ciebie;)

Zaktualizowano Rozszerzony przykład

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

}
 41
Author: MadProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-14 23:24:19

Spróbuj czegoś takiego:

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

, które ostatecznie przeskalują obraz do rozmiaru JPanel za pomocą getScaledInstance(int width, int height, ImageObserver io)

 4
Author: David Kroukamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-14 20:16:51

Dla wszystkich zainteresowanych udoskonalenie metody PaintComponent przez MadProgrammer w następujący sposób pozwala na znacznie szybszą aktualizację wyświetlacza

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

        }
 1
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-14 11:56:21

Wymyśliłem takie rozwiązanie:

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);
        }
    }
}
 1
Author: Doctor Parameter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-02 20:51:54