In this article, we show you how to draw an image in a parent container in a way that scales the image automatically to fit the container’s canvas every time the container’s size gets changed. For example: we draw an image inside a JFramewindow. When the frame is resized, the image is re-scaled automatically to fit the frame’s new size. In other words, the whole picture always gets displayed regardless of the canvas size, while maintaining the image aspect ratio. This would be useful for applications that display images of different (or unknown) sizes, e.g. images provided by the user.
Here’s source code of a utility class for drawing an image in the way mentioned above:
package net.codejava.graphics; import java.awt.Component; import java.awt.Graphics; import java.awt.Image; /** * This utility class draws and scales an image to fit canvas of a component. * if the image is smaller than the canvas, it is kept as it is. * * @author www.codejava.net * */ public class ImageDrawer { public static void drawScaledImage(Image image, Component canvas, Graphics g) { int imgWidth = image.getWidth(null); int imgHeight = image.getHeight(null); double imgAspect = (double) imgHeight / imgWidth; int canvasWidth = canvas.getWidth(); int canvasHeight = canvas.getHeight(); double canvasAspect = (double) canvasHeight / canvasWidth; int x1 = 0; // top left X position int y1 = 0; // top left Y position int x2 = 0; // bottom right X position int y2 = 0; // bottom right Y position if (imgWidth < canvasWidth && imgHeight < canvasHeight) { // the image is smaller than the canvas x1 = (canvasWidth - imgWidth) / 2; y1 = (canvasHeight - imgHeight) / 2; x2 = imgWidth + x1; y2 = imgHeight + y1; } else { if (canvasAspect > imgAspect) { y1 = canvasHeight; // keep image aspect ratio canvasHeight = (int) (canvasWidth * imgAspect); y1 = (y1 - canvasHeight) / 2; } else { x1 = canvasWidth; // keep image aspect ratio canvasWidth = (int) (canvasHeight / imgAspect); x1 = (x1 - canvasWidth) / 2; } x2 = canvasWidth + x1; y2 = canvasHeight + y1; } g.drawImage(image, x1, y1, x2, y2, 0, 0, imgWidth, imgHeight, null); } }
The drawScaledImage() is designed to be generic so it can be reused easily. To understand how this method works, let’s take a look at the following pictures:
Note that the scaled image always has the same aspect ratio as the original one.
In Java, it’s very often that we use a JLabel component to display an image via its setIcon() method. So the following class extends the JLabel component in order to use the utility class above:
package net.codejava.graphics; import java.awt.Graphics; import javax.swing.ImageIcon; import javax.swing.JLabel; /** * This is an extended version of a JLabel which draws its icon image using * the ImageDrawer utility. * * @author www.codejava.net * */ public class ScaledImageLabel extends JLabel { protected void paintComponent(Graphics g) { ImageIcon icon = (ImageIcon) getIcon(); if (icon != null) { ImageDrawer.drawScaledImage(icon.getImage(), this, g); } } }
Note that this class overrides its super’s paintComponent() method to re-draw the image icon.
And following is a demo program which is a JFrame window that allows the user to enter file path of an image then display it using the ScaledImageLabel class above:
package net.codejava.graphics; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.SwingUtilities; /** * This is a test program that draws an image provided by the user and scales * the image to fit its parent container (a JLabel). * * @author www.codejava.net * */ public class ImageFrameDemo extends JFrame implements ActionListener { private JLabel labelImgFilePath = new JLabel("Enter Image File Path: "); private JTextField fieldImgFilePath = new JTextField(20); private JButton buttonDisplay = new JButton("Display"); private JLabel labelImage = new ScaledImageLabel(); public ImageFrameDemo() { super("Image Frame Demo"); setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.insets = new Insets(10, 10, 10, 10); constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.NORTHWEST; constraints.gridy = 0; constraints.gridx = 0; add(labelImgFilePath, constraints); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; constraints.gridx = 1; add(fieldImgFilePath, constraints); constraints.gridx = 2; constraints.fill = GridBagConstraints.NONE; constraints.weightx = 0.0; add(buttonDisplay, constraints); constraints.fill = GridBagConstraints.BOTH; constraints.weightx = 1.0; constraints.weighty = 1.0; constraints.gridy = 1; constraints.gridx = 0; constraints.gridwidth = 3; labelImage.setPreferredSize(new Dimension(400, 300)); add(labelImage, constraints); buttonDisplay.addActionListener(this); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setLocationRelativeTo(null); } @Override public void actionPerformed(ActionEvent event) { String filePath = fieldImgFilePath.getText(); try { Image image = ImageIO.read(new File(filePath)); labelImage.setIcon(new ImageIcon(image)); } catch (Exception ex) { System.err.println(ex); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new ImageFrameDemo().setVisible(true); } }); } }
Here are some sample outputs when running this program:
Try to resize the frame window, and we will see the image is scaled accordingly.
Other Java Graphics Tutorials: