Search code examples
javabufferedimagejavax.imageio

How can I create a non-square BufferedImage from a non-square image file?


I'm trying to create a BufferedImage from an arbitrary image file and then center that image in the background of a JPanel. I don't have any problems with square images, but I can't figure out how to handle non-square images.

Some debugging indicates that the (immediate) problem is that when I use ImageIO to create a BufferedImage from a rectangular input file, say one that's 256x128, BufferedImage.getHeight() returns 256 rather than 128.

Here's a snippet approximating my code:

class ExtendedPanel extends JPanel {

    static final int WIDTH = 400;
    static final int HEIGHT = 400;

    BufferedImage image;

    public ExtendedPanel(File f) {
       super();
       setPreferredSize(new Dimension(WIDTH,HEIGHT));
       image = ImageIO.read(f);
    }

    @Override
    public void paintComponent(Graphics g) {
        int x = (WIDTH - image.getWidth())/2;
        int y = (HEIGHT - image.getHeight())/2;
        Graphics2D g2d = (Graphics2d)g;
        g2d.drawRenderedImage(image,AffineTransform.getTranslateInstance(x,y));
    }

}

As I said, this is fine for square image files. But with rectangular images that are wider than they are tall, the image is displayed higher than it should be. I haven't tried it yet with images taller than they are wide but I'm afraid that it that case the image would be displayed too far to the left. What can I do?


Solution

  • It is more a problem of (understanding) the right calculation.

    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2d)g;
    
        // How to scale the image:
        double xscale = ((double)WIDTH) / image.getWidth();
        double yscale = ((double)HEIGHT) / image.getHeight());
    
        // When scaling proportionally:
        double scale = Math.min(xscale, yscale); // max for covering entire panel.
        xscale = scale;
        yscale = scale;
    
        double w = scalex * image.getWidth();
        double h = scaley * image.getHeight();
        double x = (getWidth() - w) / 2;
        double y = (getHeight() - h) / 2;
        g.drawImage(img, (int)x, (int)y, (int)w, (int)h, Color.BLACK, null);
        //g2d.translate(x, y);
        //g2d.scale(xscale, yscale);
        //g2d.draw...;
    }
    

    Using the simple (scaling) version of drawImage what is needed is entirely clear.

    To be considered is proportionally scaling, filling entirely (loss of image part) or upto maximal size (seeing background).