Java: maintaining aspect ratio of JPanel background image

前端 未结 4 2051
伪装坚强ぢ
伪装坚强ぢ 2020-11-21 06:28

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 imag

相关标签:
4条回答
  • 2020-11-21 06:55

    For anyone interested ammending the PaintComponent method by MadProgrammer as follows allows much quicker display update

            @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);
    
            }
    
    0 讨论(0)
  • 2020-11-21 07:06

    Try something like 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);
            }
        }
    }
    

    which will ultimately scale the image to the JPanel's size by using getScaledInstance(int width, int height, ImageObserver io)

    0 讨论(0)
  • 2020-11-21 07:11

    I came up with this solution:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 07:13

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

    FitFill

    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 Dimensions. 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);
    
    }
    
    0 讨论(0)
提交回复
热议问题