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:

    • When the image is smaller than the canvas:

      Image Scaling 3

    • When the canvas aspect ratio is greater than the image aspect ratio:

      Image Scaling 1

    • When the image aspect ratio is greater than the canvas aspect ratio:

      Image Scaling 2

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:

    • When the image is small enough to be displayed entirely inside the frame:

      Image Frame Demo Test 1

    • When the image is quite big:

      Image Frame Demo Test 2

Try to resize the frame window, and we will see the image is scaled accordingly.

 

Other Java Graphics Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (ImageAutomaticScalingDemo.zip)ImageAutomaticScalingDemo.zip[Java source files]2 kB

Add comment

   


Comments 

#4Andri2020-05-23 11:27
Is this scaling lossless?
Quote
#3Fabio Muriana2016-12-29 05:20
Very good tutorial! Congratulations!
Quote
#2Isorgakm2016-02-25 11:29
Thanks !!! this is helpfull specaly for programmer Java Computer Science !
Quote
#1Elio2015-07-16 01:47
Hi, your web page is excellent, how can i compiled ImageFrameDemo.java ImageDrawer.java ScaledImageLabel.java from command line?, i would like to test the application result, any help is appreciated
Quote