I’m trying to get decent speed out of Java2D and it just isn’t happening. I don’t know if I’m doing it some dumb way or not, but I usually use OpenGL so I haven’t worried about it. This time, though I want to stick with Java2D if possible.
Here is a simple test app I made that creates a bunch of images and draws them while spinning them around and moving them. Basically it’s meant to be a simple stress test. But I find that even with very simple images and not very many of them I’m having really bad luck in terms of speed. If I increase window size to fill my screen, almost no matter what I end up getting only around 20 fps, which is just crazy.
I’ve got to be doing something wrong.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import java.util.ArrayList;
public class Test2 extends JFrame
{
private TestCanvas canvas;
public static final int NES_WIDTH = 256;
public static final int NES_HEIGHT = 224;
public Test2()
{
canvas = new TestCanvas();
getContentPane().add(canvas);
setSize(500,500);
setVisible(true);
}
public void run()
{
canvas.run();
}
public static void main(String[] args)
{
new Test2().run();
}
private class TestCanvas extends Canvas
{
private AffineTransform affine;
private BufferedImage image;
private int lastWidth;
private int lastHeight;
private ArrayList<Sprite> sprites;
private float fps;
private int draws;
public TestCanvas()
{
affine = new AffineTransform();
sprites = new ArrayList<Sprite>();
for (int i = 0; i < 20; i++)
{
sprites.add(new Sprite((float)(Math.random() * NES_WIDTH), (float)(Math.random() * NES_HEIGHT), (int)(Math.random() * 30+1), (int)(Math.random() * 30+1), (float)(Math.random()*5.0f+0.1f), (float)(Math.random() * Math.PI / 50.0f +0.1f)));
sprites.get(sprites.size()-1).setTarget((float)(Math.random() * NES_WIDTH), (float)(Math.random() * NES_HEIGHT));
}
try
{
image = ImageIO.read(new File(System.getProperty("user.dir") + "/RCR.jpg"));
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void paint(Graphics g)
{
if (lastWidth != getWidth() || lastHeight != getHeight())
{
affine.setToScale(getWidth() / (NES_WIDTH+0.0), getHeight() / (NES_HEIGHT+0.0));
lastWidth = getWidth();
lastHeight = getHeight();
}
Graphics2D g2 = (Graphics2D) g;
g2.setTransform(affine);
g2.drawImage(image,0,0,null);
for (int i = 0; i < sprites.size(); i++)
sprites.get(i).draw(g2);
g.setColor(Color.WHITE);
g.drawString("FPS: " + fps,150,15);
draws++;
}
public void run()
{
boolean running = true;
float target = 1000 / 60.0f;
float frameAverage = target;
long lastFrame = System.currentTimeMillis();
float yield = 10000f;
float damping = 0.1f;
long lastSecond = lastFrame;
while (running)
{
long timeNow = System.currentTimeMillis();
frameAverage = (frameAverage * 10 + (timeNow - lastFrame)) / 11;
lastFrame = timeNow;
yield += yield*((target/frameAverage)-1)*damping+0.05f;
for(int i = 0; i < yield; i++)
Thread.yield();
if (timeNow - lastSecond >= 1000)
{
fps = draws;
draws = 0;
lastSecond = timeNow;
}
repaint();
}
}
}
private class Sprite
{
private BufferedImage image;
private float x, y;
private int width, height;
private float rotation, rotationSpeed;
private float targetX, targetY, speed;
public Sprite(float newX, float newY, int wid, int hi, float shpeed, float rotSpeed)
{
x = newX;
y = newY;
width = wid;
height = hi;
speed = shpeed;
rotationSpeed = rotSpeed;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
image = gd.getDefaultConfiguration().createCompatibleImage(width,height,Transparency.TRANSLUCENT);
for (int i = 0; i < image.getWidth(); i++)
for (int j = 0; j < image.getHeight(); j++)
image.getRaster().setPixel(i,j,new int[]{(int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256)});
}
public void draw(Graphics2D g)
{
//Move to the target.
moveTarget();
//Adjust the rotation.
rotation += rotationSpeed;
g.rotate(rotation, x+width/2, y+height/2);
g.drawImage(image,(int)x,(int)y,null);
g.rotate(-rotation, x+width/2, y+height/2);
}
private void moveTarget()
{
if (x != targetX && y != targetY)
{
float xDist = targetX - x;
float yDist = targetY - y;
float dist = (float) Math.sqrt(xDist * xDist + yDist * yDist);
float newX = x + (xDist / dist) * speed;
float newY = y + (yDist / dist) * speed;
if ((x < targetX && newX > targetX) || (x > targetX && newX < targetX))
x = targetX;
else
x = newX;
if ((y < targetY && newY > targetY) || (y > targetY && newY < targetY))
y = targetY;
else
y = newY;
}
else
{
//Set a new target.
setTarget((float)(Math.random() * NES_WIDTH), (float)(Math.random() * NES_HEIGHT));
}
}
public void setTarget(float targX, float targY)
{
targetX = targX;
targetY = targY;
}
}
}
Also, I’ve tried with VolatileImages, just using fillRect to draw pixels, having a JPanel instead of a Canvas, and more, but no luck.
It’s no wonder we made LWJGL.