Here is my first solution on how to resize images with Java. I found this solution the most simpliest out there. However it has a big disadvante, it has a memory leak when used in web applications, specifically with TomCat 6. It looks like Tomcat 7 has it fixed, however I havn’t been able to test it myself.
To read more about the memory leak, here is a link to an issue:
https://issues.apache.org/bugzilla/show_bug.cgi?id=51687

/*
 * ImageResizer2D
 *
 * Usage:
 *
 * 	ImageResizer2D im = new ImageResizer2D(); //init
 *
 *
 * 	im.resize(Mixed image, int width, int height, Object interpolation, boolean quality); //resize image
 *
 * 	@image - BufferedImage - source of the image to resize
 * 	@image - String - path to the image
 * 	@image - File
 * 	@width - int - target width, pass 0 to resize by height
 * 	@height - int - target height, pass 0 to resize by width only
 * 	@interpolation - Object - image interpolation method (BICUBIC, BILINEAR, NEAREST_NEIGHBOR)
 * 	@quality - boolean - Use multi-step technique: start with original size, then scale down in multiple
 * 						 passes with drawImage() until the target size is reached
 *
 *  im.write(String fileDestination, String format); 			//write resized version of the file to drive
 *	im.write(File fileDestination, String format); 				//format how the file should be handled (jpg, gif, png)
 *  im.write(String format, HttpServletResponse response);//flushed resized version of the file to screen
 *  im.write(String format, OutputStream outputStream);		//flushed resized version of the file to screen
 *
 *	im.dispose(); //free resources, prevent memory leaks
 */

public class ImageResizer2D {

	// Available Rendering HINTS
	public static Object BICUBIC = RenderingHints.VALUE_INTERPOLATION_BICUBIC;
	public static Object BILINEAR = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
	public static Object NEAREST_NEIGHBOR = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;

	private BufferedImage image;

	public BufferedImage resize(String img, int targetWidth, int targetHeight,
			Object hint, boolean higherQuality) throws IOException {
		return resize(new File(img), targetWidth, targetHeight, hint,
				higherQuality);
	}

	public BufferedImage resize(File f, int targetWidth, int targetHeight,
			Object hint, boolean higherQuality) throws IOException {
		return resize( ImageIO.read(f), targetWidth, targetHeight, hint,
				higherQuality);
	}

	public BufferedImage resize(BufferedImage img, int targetWidth, int targetHeight,
			Object hint, boolean higherQuality) throws IOException {

		if(img == null)
			return null;

		int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
				: BufferedImage.TYPE_INT_ARGB;
		image = (BufferedImage) img;
		int w, h;

		int realW = img.getWidth();
		int realH = img.getHeight();

		double scale = getImageScale(realW, realH, targetWidth, targetHeight);

		//lets scale image in case if one of the parameter is 0
		targetWidth = (int) (realW * scale);
		targetHeight = (int) (realH * scale);

		//lets prevent resizing to a bigger image as it is
		if(targetWidth > realW || targetHeight > realH)
			return img;

		if (higherQuality) {
			// Use multi-step technique: start with original size, then
			// scale down in multiple passes with drawImage()
			// until the target size is reached
			w = realW;
			h = realH;
		} else {
			// Use one-step technique: scale directly from original
			// size to target size with a single drawImage() call
			w = targetWidth;
			h = targetHeight;
		}

		BufferedImage tmp = null;

		do {
			if (higherQuality && w > targetWidth) {
				w /= 2;
				if (w < targetWidth) {
					w = targetWidth;
				}
			}

			if (higherQuality && h > targetHeight) {
				h /= 2;
				if (h < targetHeight) {
					h = targetHeight;
				}
			}

			tmp = new BufferedImage(w, h, type);
			Graphics2D g = tmp.createGraphics();
			if (hint != null)
				g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
			g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g.drawImage(image, 0, 0, w, h, null);
			g.dispose();

			image = tmp;

			tmp.flush(); //flush resources

		} while (w != targetWidth || h != targetHeight);

		return image;
	}

	public void dispose() {
		image.flush();
		image = null;
	}

	public void write(String img, String format) throws IOException {
		write(new File(img), format);
	}

	public void write(File f, String format) throws IOException {
		if(image != null) {
			f.mkdirs();
			ImageIO.write(image, format, f);
		}
	}

	public void write(String format, OutputStream outputStream) throws IOException {
		if(image != null)
			ImageIO.write(image, format, outputStream);
	}

	public void write(String format, HttpServletResponse response) throws IOException  {
		if(image != null) {
			//prepare header to output image
			response.setContentType("image/jpeg");
			response.setStatus(200);	//set status 200, we found an image

			ImageIO.write(image, format, response.getOutputStream());
		}
	}

	private double getImageScale(int sourceWidth, int sourceHeight,
			int targetWidth, int targetHeight) {
		double scalex = (double) targetWidth / sourceWidth;
		double scaley = (double) targetHeight / sourceHeight;
		return Math.max(scalex, scaley);
	}

}

Thumbnails, increasing the quality with Blur filter

I find that thumbnails are not very good in quality after all. So I would suggest to blur them a little. Insert code below before the tmp.flush(); in the do-while loop.

// Blur image a little.
float softenFactor = 0.05f;
float[] softenArray = {0, softenFactor, 0, softenFactor, 1-(softenFactor*4), softenFactor, 0, softenFactor, 0};
Kernel kernel = new Kernel(3, 3, softenArray);
ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
tmp = cOp.filter(tmp, null);

Tomcat 6 memory leak

Reference

http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html

Dima Svirid

Software architect, JAVA, Spring, Hibernate, AngularJs, Backbone, MongoDB, Oracle. CTO and Co-Founder of Homeadnet.com

More Posts

Follow Me: