I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage. The only way I could figure out how to do that was to turn the BufferedImage into an ImageIcon, and set that to a JLabel, and add the JLabel to the JFrame. To update the image, I remove the JLabel, set it with the new BufferedImage, and re-add it to the JFrame. This makes the screen flash rapidly as you can see an empty frame in between each rendering. If I don’t remove the label, the program runs extremely slowly. How do I fix this? Should I even be using JLabels or is there a better way?
public static void createWindow() { frame = new JFrame("PlanetSim"); frame.setPreferredSize(new Dimension(WINDOW_X, WINDOW_Y)); frame.setMaximumSize(new Dimension(WINDOW_X, WINDOW_Y)); frame.setMinimumSize(new Dimension(WINDOW_X, WINDOW_Y)); foregroundLabel = new JLabel(new ImageIcon(foreground)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.add(foregroundLabel); frame.pack(); frame.setVisible(true); } public static void setForeground() { frame.getContentPane().remove(foregroundLabel); foregroundLabel = new JLabel(new ImageIcon(foreground)); frame.getContentPane().add(foregroundLabel); frame.pack(); }
I doubt the problem has to do with the loop itself, but I’m including it anyway just in case.
public void run() { long lastTime = System.nanoTime(), now; //keeps track of time on computer in nanoseconds double amountOfTicks = 60.0; //number of ticks per rendering double delta = 0; //time step long timer = System.currentTimeMillis(); //timer to display FPS every 1000 ms int frames = 0; while (running) { now = System.nanoTime(); delta += (now - lastTime) * amountOfTicks / 1000000000; lastTime = now; while(delta >= 1) { tick(); delta--; } if (running) render(); frames++; if (System.currentTimeMillis() - timer > 1000) { timer += 1000; System.out.println("FPS: " + frames); frames = 0; } } } public void render() { populateWindow(); Window.setForeground(); }
Advertisement
Answer
frame.getContentPane().remove(foregroundLabel); foregroundLabel = new JLabel(new ImageIcon(foreground)); frame.getContentPane().add(foregroundLabel); frame.pack();
I would suggest there is no need to remove/add the label or pack the frame since I would assume the Icon will be the same size every time.
All you need to do is replace the Icon of the label.
//frame.getContentPane().remove(foregroundLabel); foregroundLabel.setIcon( new ImageIcon(foreground) ); //frame.getContentPane().add(foregroundLabel); //frame.pack();
I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage
Otherwise just do the rendering in your paintComponent()
method. Swing is double buffered by default so it essentially removes the need to create the BufferedImage.
See: get width and height of JPanel outside of the class for an example of custom painting. Just update the ball count from 5 to 100 to make it more complex.