Java2D Bloom

So as you all can probably tell I have been goofing around with java2d alot lately :slight_smile: and here is as close as I can get to having real time bloom in pure java2D. ;D I used the bloom/glow filter from jhlabs.

http://www.mediafire.com/?1d0q54er1298uu3

and screen

P pauses
space changes particle color.
V for random velocity.
Q and W to change the bloom resolution…sorta

As you can guess, resizing the window greatly effects the performance as it is all software rendering.

I am using the particle system from my particles tutorial.

This is really small and could be use in java4k maybe. :clue:

I like it!

The effect looks nice.
But I think there is still room for performance improvements.
It does get choppy on medium resolutions.

I really can’t think of any huge performance boosts other then switching to opengl. You have to create a bufferedimage, get its pixels, edit the pixels, and then set them back onto the bufferedimage. It is just slow. The only way to get an reasonable performance is by drawing the image at a lower resolution, filtering it, and then redraw at the original resolution.

If you have any ideas I am all ears.

I am making a game with it now. Should be kinda fun.

Are you creating a bufferdImage everytime you draw?

Just asking because creating a BufferedImage and (even worse) getting the graphics from a BufferedImage is very slow. Compare to that making hundred of calls to methods of Graphics2D is much faster.

You can not get the pixels from a graphics object. That is why it is slow. If you could, you could do all sorts of cool effects. I have to create a buffered Image and to draw to and to perform the filter on. But this is sped up a bunch by using a VolatileImage and drawing with its graphics object then using the getSnapShot() method to draw onto a scaled down bufferedImage which is then filtered. I get reasonable performance with this.

Graphics2D g2d = (Graphics2D) bufferstrat.getDrawGraphics();
				g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				v = this.createVolatileImage(render.getWidth(), render.getHeight());
				b = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
						.getDefaultConfiguration().createCompatibleImage(render.getWidth()/scale, render.getHeight()/scale);
				b.setAccelerationPriority(1);
				v.getGraphics().fillRect(0, 0, v.getWidth(),v.getHeight());			
				renderGame(v.getGraphics());
				
		        b.getGraphics().drawImage(v.getSnapshot(),0,0,render.getWidth()/scale,render.getHeight()/scale,null);
		        b = bloom.filter(b, b);
		        
		        
		        
		        g2d.drawImage(b,0,0,render.getWidth(),render.getHeight(),null);
		        for(int j = 0; j <= powers.size() - 1;j++){
					powers.get(j).render(g2d);
				}
				g2d.dispose();

If there was some way of getting pixels in a different way I am more then interested.

Check this code out.
I made a little Applet with a similar particle effect
(just some bouncing particles)
Increase the number of particles to see how the speed behaves.
I limited the framespeed, to keep it steady with different amounts of particles.

bloomparticles - Applet

http://www.rtsmaker.com/bloomparticles.jpg


package bat4k;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Random;

/*
 * Particle Animation, by Damocles
 */
public class BloomParticles extends Applet implements Runnable
{
	//screen dimensions
	static final int WIDE = 1000;
	static final int HIGH = 800;
	//particles
	static final int MAX_PARTICLES = 6000;
	static final int MAX_FRAMES = 50;

	
	public void init()
	{
		new Thread(this).start();
	}

	public void start()
	{
		setSize(WIDE, HIGH);
	}

	public void run()
	{
		try
		{

			while (!isActive()) Thread.yield();

			BufferedImage screen = new BufferedImage(WIDE, HIGH, 1);
			int[]  pixl = ((DataBufferInt) screen.getRaster().getDataBuffer()).getData();
			Graphics g = screen.getGraphics();

			g.setColor(new Color(0x00000000));
			Graphics gCanvas = getGraphics();

			Random ran = new Random();

			//prep particle lightarray
			//x,y,fade-frames
			int[][][] particleFrame = new int[10][10][MAX_FRAMES];

			for (int i = 0; i < MAX_FRAMES; i++)
			{

				for (int x = 0; x < 10; x++)
				{
					for (int y = 0; y < 10; y++)
					{
						double dist = Math.sqrt((x - 5d) * (x - 5d) + (y - 5d) * (y - 5d));

						dist = 255 - dist * 60 - i;

						dist = dist < 0 ? 0 : dist;
						dist = dist > 255 ? 255 : dist;

						particleFrame[x][y][i] = (int) dist;
					}
				}
			}

			int[] particlesFrame = new int[MAX_PARTICLES]; //frame
			float[][] particlesBase = new float[MAX_PARTICLES][4]; //x,y , velocity x,y

			for (int i = 0; i < MAX_PARTICLES; i++)
			{
				particlesBase[i][0] = WIDE/2+ ran.nextInt(WIDE/4) - WIDE/8;
				particlesBase[i][1] = HIGH/2+ ran.nextInt(HIGH/4)- HIGH/8;

				particlesBase[i][2] = ran.nextFloat()*2 - 1f;
				particlesBase[i][3] = ran.nextFloat()*2 - 1f;

				particlesFrame[i] = ran.nextInt(MAX_FRAMES);
			}

			long lastFrame = System.nanoTime();

			while (isActive())
			{

				g.fillRect(0, 0, screen.getWidth(), screen.getHeight());

				for (int i = 0; i < MAX_PARTICLES; i++)
				{

					particlesFrame[i]++;
					if (particlesFrame[i] >= MAX_FRAMES) particlesFrame[i] = 0;

					if (particlesBase[i][2] < 0d && particlesBase[i][0] < 10) particlesBase[i][2] = -particlesBase[i][2];
					else if (particlesBase[i][2] > 0d && particlesBase[i][0] > WIDE - 15) particlesBase[i][2] = -particlesBase[i][2];

					if (particlesBase[i][3] < 0d && particlesBase[i][1] < 10) particlesBase[i][3] = -particlesBase[i][3];
					else if (particlesBase[i][3] > 0d && particlesBase[i][1] > HIGH - 15) particlesBase[i][3] = -particlesBase[i][3];

					particlesBase[i][0] += particlesBase[i][2];
					particlesBase[i][1] += particlesBase[i][3];

					int px = (int) particlesBase[i][0];
					int py = (int) particlesBase[i][1];

					for (int x = 0; x < 10; x++)
					{
						for (int y = 0; y < 10; y++)
						{

							int pc = (pixl[WIDE * (y + py) + x + px]) & 0xFF;
							pc += particleFrame[x][y][particlesFrame[i]];
							pc = pc > 255 ? 255 : pc;
							int pc2 = (pc > 230) ? pc : 0;

							pixl[WIDE * (y + py) + x + px] = pc | (pc << 8) | (pc2 << 16);

						}
					}
				}

				gCanvas.drawImage(screen, 0, 0, null);

				//limit framespeed
				do
				{
					Thread.yield();
				} while (System.nanoTime() - lastFrame < 0);
				lastFrame += 33 * 100 * 100 * 100;

				//use this without framelimited
				//Thread.yield();
			}

		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

}


Keeps my cpu usage at 25% cant go higher without multithreading. Interesting way of doing things. My effect would be greatly diminished if I did not scale down the size. By scaling down the size, it makes the effect more exaggerated and much faster but you lose quality.

Try to never squeeze out all your PCs performance. Its likely that you have a better PC than the average user.
And a game usually gets slower during development.

I try to keep a big enough buffer, and test on a relatively slow System.

Anyway, directly manipulating the pixels (((DataBufferInt) screen.getRaster().getDataBuffer()).getData():wink:
of the Raster is quite performant.

Very important is to keep the main rendering loop as short and direct as possible.
Dont do calculations there that you can buffer beforehand.
And dont create any Objects! only reference them

The JHLabs filters don’t offer very good performance for animation. They go through various hoops to keep the BufferedImage from being unmanaged - which is a pointless exercise when you’re updating them every frame. You should create 2 BufferedImages too, otherwise the filter will create a temporary image and do an unnecessary copy every frame. You should also probably not use the createCompatibleImage() method for this either, as the filter is converting the data to ARGB.

Your best bet is to create 2 ARGB BufferedImages, grab the data arrays like @Damocles suggests, and fork the filter code so that you can pass in the input and output arrays rather than BufferedImages. That should speed things up a bit.

You can also use optimized algorithm like in this paper: An efficient algorithm for Gaussian blur using finite-state machines

NOTE: That optimization and multi-dimensional arrays (in java) are mutually exclusive.

Not true, nobody sane uses multidimensional arrays in Java, you just index it manually in single dimensional array.

I don’t understand. You say “not true”, then repeat the same thing I said in different words.

@Roquen - where did multi-dimensional arrays come from?

@jezek2 - is there a Java implementation of that anywhere?

Maybe he referrs to my example code.
I know that single dimensional arrays are quicker. (around 30% for the pure lookup logic)
But I find that is an optimization that can be done in the end. During coding I see multi-dim arrays as much nicer to read and edit.

Things like making a multi to a single array is just mechanical optimization when cleaning up the program.

the main point of the example was to show how much faster direct pixel manipulation is

Oh sorry, thought you meant the optimization in the paper I posted link to.

What kind of dumb question is that? The pseudocode is trivial (and the text describing it is quite simple too), so you shouldn’t have any problem translating it to Java…

A TL;DR one! :wink: Have filed away for later perusal. Still, no matter how simple, it’s always useful when someone’s already done the work for you! ;D

Thank you all for the advice.

When I said 25% cpu I meant that is what your app was running at. I try to keep my stuff running around 5-8% cpu or lower. Currently, this bloom filter runs at 7-15 depending on how many particles there are. I changed the filter so it takes int[] of the pixels and outputs int[], this has sped things up a little I am very impressed. I am still really new to a lot of more complex things when it comes to coding. Here is what I have decided to make after having some fun with this thing.

http://s11.postimage.org/b3smmzfg3/png1.png

Its all done without any images and is very small. Kinda break it styled. Very hard to play because there is so much going on. Lots and lots of glowing particles to distract you from the glowing ball.

The screen looks really nice.