# Graphics acceleration in java

matiasmorant
I have made a java code which displays a blue sphere and lets you control with the mouse where its illumination comes from. (its actually a circle and some parts are painted white/blue/black depending on the mouse position to make it look 3D)

here is the code

import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;

public class esfera extends JFrame{

double azimuth=3*Math.PI/4, inclination=Math.PI/4; //angle coordinates for the 'light source'

private BufferedImage image = new BufferedImage(800,800,BufferedImage.TYPE_INT_RGB); //the image storing the sphere drawing

public esfera(){ //initializer adding a listener to mouse motion
public void mouseDragged(MouseEvent e){
azimuth=((800-e.getX())/800.00)*Math.PI;
inclination=(e.getY()/800.00)*Math.PI;
repaint();
}
});}

public void paint(Graphics g){ //does the whole painting

g.drawImage(image,0,0,null); //paint the current image to the frame

for(int i=0;i<800;i++){ //make next image, pixel by pixel
for(int j=0;j<800;j++){
image.setRGB(i,j,color((i-400)/200.00,(400-j)/200.00));
}
}
}

public int color(double x, double y){ //this is what color each pixel should be painted in order to make the picture look like a sphere
double clr;
double z = Math.sqrt(1-(x*x+y*y));
double angle = Math.acos(Math.cos(inclination)*y+Math.sin(inclination)*(Math.sin(azimuth)*z+Math.cos(azimuth)*x));

if(x*x+y*y>1) clr=0;
else if (angle>Math.PI/2) clr=0;
else if (angle>Math.PI/4)clr=255*(2-angle/(Math.PI/4));
else{ int white =((int)(255*(1-angle/(Math.PI/4)))<<8)+((int)(255*(1-angle/(Math.PI/4)))<<16);
clr=white+255;}

return (int)clr;

}

public static void main(String[] args){ //the main method of course

esfera frame = new esfera();
frame.setSize(800,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

}

it works ok. the only problem is that when you move the mouse it takes too long to update the screen with the next picture.(so the 'light source' seems to be jumping from one place to another if you move the mouse too fast)

the question is: how can I fix this problem?

i have been reading about VolatileImage but it seems that you can't write a VolatileImage pixel by pixel, so it's not what i need.

i have also read about BufferStrategy, but i'm not sure if it could help me or not, (didn't completely understand how to use it neither)

could you help me please??? i've been trying to solve it all day...

Mentor
Java is probably not the best choice for drawing figures on the screen that need to be frequently updated. The reason for this is that your code gets compiled into Java byte codes that are then translated into machine instructions at run time. For graphics programming, compiled languages rather than interpreted languages (like Java) are usually preferred.

The way programmers did things in the "old days" was to work with several screen buffers that they would swap into and out of screen memory. While one buffer was being updated, another screen buffer that was ready to be viewed was swapped into memory. I don't know if you could do this in Java.

Homework Helper
Java has a "double buffering" option that lets you create the next image completely before you display it. That will get rid of any "flickering" as you move the images but it won't make the app run faster. http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html

You could do some optimisation on color() and the way you call image.setRGB().

A simple speed-up for color() would be to do the test
Code:
if(x*x+y*y>1) clr=0;
first, before you calculate the expensive square roots and trig functions.
Also asimuth and incliation don't change, so you don't need to recalculate the same sines and cosines 640,000 times for each pixel of the image

If that isn't enough, you could limit the number of calls to color().

Apparently everything outside of the sphere is black, so you could set the complete image to black with one call, not pixel by pixel. Then you could limit the for loops to cover the image. Something like
Code:
Find imin and imax that cover the image
for i  = imin to imax {
find jmin and jmax that cover line i of the image
for j = jmin to jmax {
color(.....)
}
}

matiasmorant
it originally took 500-600-ms to update the image
doing the if(x*x+y*y>1) clr=0; test first, it only took 300-250ms
avoiding the recalculation of asimuth and incliation, 200-225 ms

the image is initializated to black by default, so recalculating pixel by pixel only the smallest square fitting the sphere, it takes 125 ms to update

:O thanks a lot!!

Homework Helper
it originally took 500-600-ms to update the image
doing the if(x*x+y*y>1) clr=0; test first, it only took 300-250ms
avoiding the recalculation of asimuth and incliation, 200-225 ms

the image is initializated to black by default, so recalculating pixel by pixel only the smallest square fitting the sphere, it takes 125 ms to update

:O thanks a lot!!

Take-home message: good programming is about attention to detail.

I bet you can make it run quite a bit faster than 8 frames per seond, if you do some more thinking. Hint: there seems to be lot of converting real values to integers and back again...

Mentor
also don't underestimate java as far as speed goes as its JVM and hotspot compiling have really sped things up considerably though it will never match a finally crafted C or asm program.

we use Java a lot for our realtime charting and displays and it works just fine. Ray tracing is very cpu/graphics intense and thats why gamers like to have high performance graphics card with their own built-in graphics cpu and games that support it.

Edgardo
it originally took 500-600-ms to update the image
doing the if(x*x+y*y>1) clr=0; test first, it only took 300-250ms
avoiding the recalculation of asimuth and incliation, 200-225 ms

the image is initializated to black by default, so recalculating pixel by pixel only the smallest square fitting the sphere, it takes 125 ms to update

:O thanks a lot!!

Can you post your new code?

matiasmorant
here's the new code :)

import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;

public class esfera extends JFrame{

double azimuth=3*Math.PI/4, inclination=Math.PI/4; //angle coordinates for the 'light source'
double cosAzimuth=Math.cos(azimuth) ,sinAzimuth=Math.sin(azimuth), cosInclination=Math.cos(inclination),sinInclination=Math.sin(inclination);

private BufferedImage image = new BufferedImage(800,800,BufferedImage.TYPE_INT_RGB); //the image storing the sphere drawing

public esfera(){ //initializer adding a listener to mouse motion
public void mouseDragged(MouseEvent e){
azimuth=((800-e.getX())/800.00)*Math.PI;
inclination=(e.getY()/800.00)*Math.PI;
cosAzimuth=Math.cos(azimuth);
sinAzimuth=Math.sin(azimuth);
cosInclination=Math.cos(inclination);
sinInclination=Math.sin(inclination);
repaint();
}
});}

public void paint(Graphics g){ //does the whole painting

g.drawImage(image,0,0,null); //paint the current image to the frame

for(int i=200;i<600;i++){ //make next image, pixel by pixel
for(int j=200;j<600;j++){
image.setRGB(i,j,color((i-400)/200.00,(400-j)/200.00));
}
}
}

public int color(double x, double y){ //this is what color each pixel should be painted in order to make the picture look like a sphere
double clr;

if(x*x+y*y>1) clr=0;
else {
double z = Math.sqrt(1-(x*x+y*y));
double angle = Math.acos(cosInclination*y+sinInclination*(sinAzimuth*z+cosAzimuth*x));

if (angle>Math.PI/2) clr=0;
else if (angle>Math.PI/4)clr=255*(2-angle/(Math.PI/4));
else{ int white =((int)(255*(1-angle/(Math.PI/4)))<<8)+((int)(255*(1-angle/(Math.PI/4)))<<16);
clr=white+255;}

}

return (int)clr;

}

public static void main(String[] args){ //the main method of course

esfera frame = new esfera();
frame.setSize(800,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

}