Rotate image in memory by an arbitrary angle

Today I stumbled over a question which answer seemed to be easy: How to rotate an image in memory by an arbitrary angle? I had to find out that most examples out there are only for square images, rotations in 90 degree steps or are simply not working at all. It looks like most code snippets were copied from one to another without ever testing it!

This bothered me so much, that I sat down and worked out an example that really works.

It rotates an image by an arbitrary angle in degrees, with positive values to rotate clockwise and negative values to rotate counterclockwise.

The tricky part was to calculate the new image size, that fits exactly (well, as exactly as Java’s double can) the rotated image.

So, here it is:

	/**
	 * Rotates an image by an arbitrary amount of degrees around its center. A
	 * positive value for <code>degrees</code> will rotate the image clockwise,
	 * a negative value counterclockwise. An image will be returned with a size
	 * that exactly fits the rotated image.
	 * 
	 * @param src
	 *            The source image to rotate.
	 * @param degrees
	 *            The amount in degrees to rotate, a positive value rotates
	 *            clockwise, a negative counterclockwise.
	 * @return A new <code>BufferdImage</code> with a new size that exactly fits
	 *         the rotated image.
	 */
	public static BufferedImage rotateImage(BufferedImage src, double degrees) {
		double radians = Math.toRadians(degrees);

		int srcWidth = src.getWidth();
		int srcHeight = src.getHeight();

		double sin = Math.abs(Math.sin(radians));
		double cos = Math.abs(Math.cos(radians));
		int newWidth = (int) Math.floor(srcWidth * cos + srcHeight * sin);
		int newHeight = (int) Math.floor(srcHeight * cos + srcWidth * sin);

		BufferedImage result = new BufferedImage(newWidth, newHeight,
				src.getType());
		Graphics2D g = result.createGraphics();
		g.translate((newWidth - srcWidth) / 2, (newHeight - srcHeight) / 2);
		g.rotate(radians, srcWidth / 2, srcHeight / 2);
		g.drawRenderedImage(src, null);

		return result;
	}
Advertisement

The Unparseable Date Pitfall

Here is another pitfall with the SimpleDateFormat class.
Unlike my previous blog entry, this pitfall is not a multithreading issue; the following code is thread-safe. However, it can fail in certain situations.

package com.udojava.blog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ParseDateDemo {

	public static void main(String[] args) throws ParseException {
		String dateString = "Thu Oct 25 6:12 pm PDT 2012";

		SimpleDateFormat dateFormat = new SimpleDateFormat(
				"EEE MMM d h:mm a z yyyy");

		Date theDate = dateFormat.parse(dateString);

		System.out.println(dateFormat.format(theDate));
	}

}

The code will run on a lot of systems without ever failing, but on certain system setups, it will fail.

For example, on my computer it won’t run and instead produce the following exception:

Exception in thread "main" java.text.ParseException: Unparseable date: "Thu Oct 25 6:12 pm PST 2012"
at java.text.DateFormat.parse(DateFormat.java:357)
at com.udojava.blog.ParseDateDemo.main(ParseDateDemo.java:15)

This is simply because my system is set up with a German locale setting, which does not understand the English names for the day (Thu) and month (Oct). The code will fail on all systems, which do not run under an English locale setup.

You can test it, by giving the Java arguments –Duser.country=DE –Duser.language=de when starting the program. This will set the Java default locale to German/Germany.

Now how to fix it? Starting the program with –Duser.language=en and forcing the users to use your locale is certainly not a good way to go.

Luckily, we can take care that the SimpleDateFormat class works with the correct locale, simply by adding a locale parameter to the constructor:

		SimpleDateFormat dateFormat = new SimpleDateFormat(
				"EEE MMM d h:mm a z yyyy", Locale.ENGLISH);

Now the program will run correctly, no matter what default locale Java is using.

The “write once, run anywhere” slogan from Java needs most of the time more care by the programmer than just using the Java programming language.

Java SimpleDateFormat gives wrong results

Would you expect the following lines of code to ever print something to the standard output?

public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
...
Date date = dateFormat.parse("2012-10-26");
String reformat = dateFormat.format(date);
if (!"2012-10-26".equals(reformat)) {
  System.out.println("Wrong date: " + reformat);
  }

Indeed, there are situations when such code will occasionally fail.
The SimpleDateFormat class is not thread safe, that means whenever two or more separate threads access the same class instance, the results from the parse() and format() methods can produce wrong results.
To reproduce this effect, I show the full test code:

package com.udojava.blog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatDemo {

	public static SimpleDateFormat dateFormat = new SimpleDateFormat(
			"yyyy-MM-dd");

	public class DemoThread extends Thread {

		@Override
		public void run() {
			for (int i = 0; i < 100; i++) {
				try {
					Date date = dateFormat.parse("2012-10-26");
					String reformat = dateFormat.format(date);
					if (!"2012-10-26".equals(reformat)) {
						System.out.println("Wrong date: " + reformat);
					}
				} catch (ParseException e) {
					e.printStackTrace();
				}
			}
		}

	}

	public void testRun() {
		DemoThread t1 = new DemoThread();
		DemoThread t2 = new DemoThread();
		t1.start();
		t2.start();
	}

	public static void main(String[] args) {
		new DateFormatDemo().testRun();
	}

}

If you run this, you will get differing outputs from one run to the next run, something like this:

Wrong date: 2201-10-26
Wrong date: 2201-10-26
Wrong date: 2012-12-26
Exception in thread "Thread-1" java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1011)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2088)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at com.udojava.blog.DateFormatDemo$DemoThread.run(DateFormatDemo.java:18)

So, sometimes it can be dangerous to try to over-optimize by using static members that are shared between threads. Even simple things like a SimpleDateFormat can fail in multithreading.
And they usually then fail only in 1 out of 1000 times, making it a real horror to find, debug and fix those issues.

JMX enable your software

When you are writing software that runs in the background, you will sooner or later have the need to monitor and/or administrate it. There are several ways to add this feature to your program, one of the easiest is to use the Java Management eXtension, JMX in short.
JMX is included since Java 1.5 with a lot of extensions in Java 1.6. If you plan to use it, I recommend using at least Java 1.6.
To put it shortly, every Java program has a built-in service (the JMX agent), which allows a separate program (the JMX client) to monitor it and perform administrative tasks. Clients can connect to your program in different ways, allowing local and remote connections. Most professional and public domain administration/monitoring applications now support JMX. There are also bridges between SNMP (Simple Network Management Protocol) and JMX. Java (the JDK) comes with two administration clients, the older JConsole and the newer JVisualVM.

How to JMX enable your software

To enable your software to be managed by JMX clients, you simply have to register one or more so called Managed Beans (MBeans) with the Java engine. Simply said, an MBean is a Java class that gives a JMX client access to methods and data, you implement.
To show this in a working example, I will create a simple Java program that has a state that can be changed by entering a new state string in the console.

(Listing 1, Test server)

package com.udojava.blog.jmx;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class SimpleTestServer {

	private String serverState = "started";

	public String getServerState() {
		return serverState;
	}

	public void setServerState(String serverState) {
		this.serverState = serverState;
	}

	public void startServer() throws IOException {
		String input = "";
		BufferedReader console = new BufferedReader(new InputStreamReader(
				System.in));
		while (!input.equalsIgnoreCase("q")) {
			System.out.println("Enter new state, q to quit:");
			input = console.readLine();
			setServerState(input);
			System.out.println("New state: " + getServerState());
		}
		System.out.println("Aborted from console.");
	}

	public static void main(String[] args) throws IOException {
		SimpleTestServer server = new SimpleTestServer();
		server.startServer();
	}

}

In the next step, I will allow a JMX client to show and change the state and also to shut down the program. I will define a managed bean that will do this all. A managed bean is made up by a Java interface, which defines the contract and a Java class that actually implements the interface.

(Listing 2, Interface)

package com.udojava.blog.jmx;

public interface SimpleTestAdminMBean {
	public void setServerState(String newState);

	public String getServerState();

	public void shutDownServer(String reason);
}

In the interface, I have defined the three operations for reading and writing the server state and for shutting down the server. Note that the shutdown method also has a parameter, where the JMX client can give a reason for the shutdown.

(Listing 3, Bean class)

package com.udojava.blog.jmx;

public class SimpleTestAdmin implements SimpleTestAdminMBean {

	private SimpleTestServer server;

	public SimpleTestAdmin(SimpleTestServer theServer) {
		this.server = theServer;
	}

	@Override
	public void setServerState(String newState) {
		System.out.println("Setting new state through JMX: " + newState);
		server.setServerState(newState);
	}

	@Override
	public String getServerState() {
		return server.getServerState();
	}

	@Override
	public void shutDownServer(String reason) {
		System.out.println("Shutting down through JMX, reason: " + reason);
		/*
		 * Shut down in a separate thread, so that this method
		 * can return before the program exits.
		 */
		new Thread(new Runnable() {

			@Override
			public void run() {
				System.exit(0);
			}
		}).start();
	}

}

Notice that I also defined a constructor that takes the server instance as a parameter, so that I can access the server functionality from the bean. By convention, the bean interface has a name that is the implementation class name plus the extension MBean.
In the last step, I will create and register the managed bean with the built in JMX server. To do this, I add a few lines to the startServer() method of my server class.

(Listing 4, JMX enabled server)

	public void startServer() throws IOException {
		/*
		 * Register JMX bean
		 */
		MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
		ObjectName mxBeanName;
		try {
			mxBeanName = new ObjectName(
					"com.udojava.blog.jmx:type=SimpleTestAdmin,name=Server Info");
			SimpleTestAdmin mxBean = new SimpleTestAdmin(this);
			mbs.registerMBean(mxBean, mxBeanName);
		} catch (Exception e) {
			System.out.println("Error registering JMX");
			e.printStackTrace();
		}

		String input = "";
		BufferedReader console = new BufferedReader(new InputStreamReader(
				System.in));
		while (!input.equalsIgnoreCase("q")) {
			System.out.println("Enter new state, q to quit:");
			input = console.readLine();
			setServerState(input);
			System.out.println("New state: " + getServerState());
		}
		System.out.println("Aborted from console.");
	}

Note that the bean name is not just a simple name string, but an object that is constructed using a domain name, a type and a name. This syntax follows the JMX standard to name managed bean instances.
Testing with a JMX client
First start the demo server, and then go to your Java JDK installation directory. In the bin folder, you will find two JMX clients, JConsole and JVisualVM. When you start them, each one will present you with a list of local running Java programs. Go to the MBeans tab and you can browse there to the defined bean and set/get the server state and shutdown the server from JConsole. With JVisualVM you probably will need to install the MBean plugin first. In that case go to the tools menu, select plugins and available plugins, select the MBeans plugin and follow the instructions.

JConsole MBeans view

Some links for more information:
The Java JMX Tutorial
JVisualVM Home

%d bloggers like this: