How to create a thumbnail of an image in Java without using external libraries

I had a situation when I wanted to display thumbnails of the images that my user had uploaded to my web application. A straightforward way could be to return the images in its entirety and use css techniques to scale down the image into a thumbnail. However, this is costly for my users who mainly access my web application from mobile devices.

One strategy for optimizing the web experiences of my users would be to generate the thumbnails at the server end so as to reduce the sizes of the HTTP responses that my server sends back to the mobile devices. And to reduce bloat from external libraries, I decided to use the facilities provided from Java standard libraries.

This post documents how I create a thumbnail from an image without using external Java libraries. For the sake of brevity, let's assume that we had downloaded an image via HTTP GET from a web server and saved that image to the path /images/sample.jpg.

Reading an image from a file

BufferedImage originalBufferedImage = null;
try {
    originalBufferedImage = ImageIO.read(new File("/images/sample.jpg"));
}	
catch(IOException ioe) {
    System.out.println("IO exception occurred while trying to read image.");
    throw ioe;
}

We first use the read method of the javax.imageio.ImageIO class to read our image from the hard disk. If we are able to read the image, we will get a java.awt.image.BufferedImage object for us to manipulate the image.

Scaling the image to a size which is slightly larger than the thumbnail

After we have the BufferedImage object, we will then scale the original image to a size which is slightly larger than the thumbnail which we want to create.

int thumbnailWidth = 150;

int widthToScale, heightToScale;
if (originalBufferedImage.getWidth() > originalBufferedImage.getHeight()) {

    heightToScale = (int)(1.1 * thumbnailWidth);
    widthToScale = (int)((heightToScale * 1.0) / originalBufferedImage.getHeight() 
                    * originalBufferedImage.getWidth());

} else {
    widthToScale = (int)(1.1 * thumbnailWidth);
    heightToScale = (int)((widthToScale * 1.0) / originalBufferedImage.getWidth() 
                    * originalBufferedImage.getHeight());
}

The above code assumed that the thumbnail image that we what to create from the image has a width of 150px and we want to scale the original image to a width or height of 10 percent longer than the width of the thumbnail.

Depending on whether the original image is portrait or landscape, we will have the appropriate width and height to scale the original image down to another one with the same aspect ratio.

BufferedImage resizedImage = new BufferedImage(widthToScale, 
    heightToScale, originalBufferedImage.getType());
Graphics2D g = resizedImage.createGraphics();

g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

g.drawImage(originalBufferedImage, 0, 0, widthToScale, heightToScale, null);
g.dispose();

We first create a new BufferedImage object to hold the scaled down version of the original image. We then get its Graphics2D object to draw a scaled version of the original image into the BufferedImage object.

Cropping the center of the resultant image

With the resized image, we can then proceed to crop our desired thumbnail from its center. We first calculate the upper left coordinate of the resized image where we want to cut our thumbnail from.

int x = (resizedImage.getWidth() - thumbnailWidth) / 2;
int y = (resizedImage.getHeight() - thumbnailWidth) / 2;

if (x < 0 || y < 0) {
    throw new IllegalArgumentException("Width of new thumbnail is bigger than original image");
}

If we have a negative x or y, it will mean that we are trying to create a thumbnail which is bigger than the original image. When that is the case, we will throw an IllegalArgumentException.

When we have a valid x and y coordinate, we call the getSubImage function to get the thumbnail:

BufferedImage thumbnailBufferedImage = resizedImage.getSubimage(x, y, thumbnailWidth, thumbnailWidth);

Saving the BufferedImage that contains the thumbnail as a file

After we have the thumbnail image in memory, the final step will be to save the thumbnail image to file

try {
	ImageIO.write(thumbnailBufferedImage, "JPG", new File("/images/sample_thumbnail.jpg"));
}
catch (IOException ioe) {
	System.out.println("Error writing image to file");
	throw ioe;
}

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.