Posted by: Morten Nobel-Jørgensen | February 13, 2010

Real time mandelbrot in Java – Part 1 (Java2D)


In this blog I’ll describe how to draw a Mandelbrot using Java2D.

An example of how 'amazing' a fractals could look on C64. Link

The power of modern computers is something that has fascinated me since childhood. I remember when an older friend of mine borrowed my Commodore 64 to render a Mandelbrot fractal. After hours of calculations my computers 1 MHz CPU finally finished and the result was … so disappointing. The screen was covered with pixels in a single color – my friend had found a very boring coordinate set to render.

A lot has happened to technology since my first experience with Mandelbrot fractals. Most important is floating point support in the CPU, faster clock-cycles (actually faster technology in general), multi-core processors and GPUs.

Simple implementation

The cool thing about the Mandelbrot fractal is that the algorithm is so simple. Basically you need to calculate the number of iterations for each pixel:

private int calculateMandelbrotIterations(float x, float y) {
	float xx = 0.0f;
	float yy = 0.0f;
	int iter = 0;
 	while (xx * xx + yy * yy <= 4.0f && iter<MAX_ITERATIONS) {
 		float temp = xx*xx - yy*yy + x;
 		yy = 2.0f*xx*yy + y;
 		xx = temp;
		iter ++;
	}
	return iter;
}

You may find it strange that a pixel is represented by two floats rather than two integers. The reason is that a pixel on your screen (two integers) is mapped to two floats in the Mandelbrot set’s coordinate system. The Mandelbrot set is defined between -2 and 2 for both x and y.

Color mapping in my mandelbrot program

You still need a way to transform your pixel into a color. To get a visual appealing result I have used the following approach:

  • 0 iterations = black
  • 1 iteration = blue
  • 16 iterations = white
  • 32 iterations = yellow
  • 64 iterations = red
  • 128 iterations = blue
  • 256 iterations = white
  • etc.

I then continue to double the iterations and cycle color between blue, white, yellow and red. The color of iterations in between are interpolated (using the previous and next color). In Java this function looks like this:

private static final float[] blue = Color.blue.getRGBComponents(null);
private static final float[] white = Color.white.getRGBComponents(null);
private static final float[] yellow = Color.yellow.getRGBComponents(null);
private static final float[] red = Color.red.getRGBComponents(null);
private static final float[][] colorCycle = {blue, white, yellow, red};
// how many iterations the first color band should use (2nd use the double amount)
private static final int colorResolution = 16;
private int getColor(int iterations) {
 	if (iterations==settingsCopy.getIterations()){
 		return 0; // black
 	}
 	float[] from;
 	float[] to;
 	int colorIndex = 0;
 	int iterationsFloat = iterations;
 	int colorRes = colorResolution;
	while (iterationsFloat>colorRes){
 		iterationsFloat -= colorRes;
 		colorRes = colorRes<<1;
 		colorIndex ++;
 	}
 	from = colorCycle[colorIndex%colorCycle.length];
 	to = colorCycle[(colorIndex+1)%colorCycle.length];
 	float fraction = iterationsFloat/(float)colorRes;
 	int[] res = new int[3];

 	// interpolate between from and to color
 	for (int i=0;i<3;i++){
 		float delta = to[i]-from[i];
 		res[i] = (int) ((from[i]+delta*fraction)*255);
 	}
 	return ((res[0])<<16)+((res[1])<<8)+(res[2]);
 }

This code actually performs quite well. To create a mandelbrot fractal with the size of 800 x 800 pixels with the maximum of 128 iterations takes around 100 ms on my machine. This gives 10 frames per second – close to real time rendering. You can use the following Java WebStart application to see how your machine performs:

Full source code is available here:
http://www.nobel-joergensen.com/java/projects/mandelbrot_jogl/javamandelbrot_src.zip

Implementation details

The color mapping is precomputed to an array of colors from 0 to max iterations, this makes the color lookup very fast.

I use a BufferedImage with a WriableRaster to create the Mandelbrot fractal on. After the Mandelbrot calculation the image is rendered to the screen. If the component is resized, the BufferedImage and its WriteableRaster are resized accordingly.

The “camera motion” (or what part of Mandelbrot is currently visible) is calculated using a different Thread (MandelbrotAnimation), that interpolates between 14 points of interest – all with a (center) location and a given zoom level. To make the camera motion more smooth I use an easyBoth-algorithm that slows down the beginning and end of each motion from one point to another.

The MandelbrotAnimation thread updates a MandelbrotSetting bean. I intentional ignore any concurrency problem, since the values are updated in very small steps, so the concurrency problems would not be visible.

Next …

As you might has guessed, if you have tried the Mandelbrot WebStart application, part 2 will describe how to boost the performance using the GPU to calculate the Mandelbrot (by creating a shader in GLSL and enable it using Java binding for OpenGL – JOGL).

Useful links

Mandelbrot Set (Wikipedia)


Responses

  1. A little call to JFrame.setDefaultCloseOperation would help 🙂


Leave a comment

Categories