Double Buffering and Active Rendering in Java with Swing Integration
Also, please check out the passive rendering guide, Double Buffering and Passive Rendering in Java using a Timer object, or go back to the tutorial's main page.
Double Buffering and Active Rendering in Java with Swing Integration
,
Article, guide, and code written by James Murphy, May 16 2010, with updates on July 20 2012.
Active rendering is a programming technique in which the graphics are constantly repainted to the screen in a loop. The opposite of active rendering is passive rendering, where an outside force decides when the program should request (might not be answered) a repainting. The advantage of this is that it allows you to achieve the highest frame rate that you can, and at a consistent rate so your animations always stay smooth.
In this guide I’ll show and explain examples in producing an actively rendered Java program with Swing being actively rendered as well. The main issues I’ll cover thoroughly are the following:
- Properly set the width and height of a JFrame using the Insets object or a Canvas object.
- Double buffering and active rendering via BufferStrategy object on a JFrame instead of a Canvas
- Using a high resolution timer to time our animations and movements
- Having graphics painted at the correct locations with ease, such as point 0,0 being the top left corner inside the application window’s borders, and having those graphics stretch as you resize the frame.
- Seamlessly integrate Swing components and actively render them on top of your application's graphics.
- Overriding the RepaintManager's functionality so that Java won't repaint on its own
- Stopping the application from over using the processor
- Creating the game (update) loop
- Why I didn't use Canvas, and why you might want to
- Advantages and disadvantages
- The main code example
Be sure to check the bottom of this page where you can view and download my entire Java program using all of the techniques I will mention.
![]() |
Properly set the width and height of a JFrame
Active rendering is mainly used in programming video games, and one thing we want to do in video games is make sure that the dimensions of the window are set as accurately as possible, especially if you do not allow the frame to be resized. When you create a JFrame with a set width and height, some of that width and height goes to the title bar and the borders of the application window. In a full screen program, this of course does not apply as there are no decorative surroundings. To fix the window to have a drawing space within the application window that matches what you originally set the JFrame to you would retrieve the Inset object of the JFrame and resize it according to how much space in pixels the insets take up.
// Correct change width and height of window so that the available // screen space actually corresponds to what is passed, another // method is the Canvas object + pack() setSize(width, height); insets = this.getInsets(); int insetWide = insets.left + insets.right; int insetTall = insets.top + insets.bottom; setSize(getWidth() + insetWide, getHeight() + insetTall);
Another method to accomplish this is to create a Canvas object (which if you used it, you would use a BufferStrategy on that instead of the JFrame itself) with a size that matches what you want window real estate to be (the area that you can draw on), and then add the Canvas object to the JFrame, finishing with a call to the JFrame’s pack() method. Pack() would expand the JFrame to contain the whole Canvas within its windows borders, thus achieving the same effect as retrieving the Insets.
// Remember, there's some slightly issues mixing AWT and Swing in Java 6 and earlier // More importantly, a canvas is not a container, so you cannot add/draw Swing components // on top of it, unless you use layered panes. Canvas canvas = new Canvas(); Canvas.setSize(400, 300); frame.add(canvas); frame.pack();
Double buffering and active rendering via BufferStrategy object on a JFrame
The main object to accomplish double buffering with active rendering is the BufferStrategy object. This small section from the main code example shows how to retrieve the BufferStrategy inside a JFrame. (Take notice that in the code example at the bottom of this page, variable bufferStrategy is of a declared BufferStrategy object elsewhere in code.)
// create a buffer strategy using two buffers createBufferStrategy(2); // set this JFrame's BufferStrategy to our instance variable BufferStrategy bufferStrategy = getBufferStrategy();
You can only create a BufferStrategy from Window or Canvas objects, or any of their subclasses. Another word you will hear being used is page flipping. Page flipping is another buffering technique that uses two buffers, a display buffer, and a back end buffer that's being drawn too. These two buffers work together by simply only switching pointers to switch roles between game renders. Page flipping is more efficient than double buffering, and luckily the BufferStrategy class decides for us what technique we should use, by first trying to use page flipping, and if that doesn't work out, then it uses double buffering.
Inside the active rendering loop, the following code should exist. In the following code note the call to draw(Graphics), in the main code example this is a method I created to separate functionality within the program.
// Then after everything is updated, we can draw what we updated
Graphics2D g = null;
try
{
g = (Graphics2D) bufferStrategy.getDrawGraphics();
draw(g); // enter the method to draw everything
}
finally
{
g.dispose();
}
if (!bufferStrategy.contentsLost())
{
bufferStrategy.show();
}
// The sync call prevents possible event queue problems in Linux,
// I'm not sure if this call is needed anymore, it run's fine
// on my Linux machines going back to Ubuntu 8.04.
// In addition, this call is quite costly.
// Decomment it and see for yourself how much FPS drops.
// My 2.2ghz laptop running clocks this call taking between
// 4 to 5 milliseconds.
// Toolkit.getDefaultToolkit().sync();
The call to “Toolkit.getDefaultToolkit().sync()” exists to fix event queue problems, although I’ve never ran into a problem yet without it, and I'm not sure if the problem still exists. The last time I've heard that it may be needed was in a book from 2003.
Another way to set up a BufferStrategy is to use the Canvas class. Like I mentioned above, the Canvas class can also be used to fix the JFrame’s height. One thing to point out is that Canvas is an AWT object, and JFrame is a Swing object, mixing AWT and Swing can create faulty results. I've haven't experimented using a Canvas and Swing together, so if you do go that route, this may explain some funky results you could get. More importantly though a Canvas is not a container, so you will not be able to add Swing components on top of it as naturally as we do here, unless if you use some sort of a layered pane combination with a Canvas for the application's graphics, and a transparent JPanel for the Swing components.
Using a high resolution timer
To time our animations and movements perfectly, we must find out how much time has elapsed. Before Java 1.5, the easiest way to do this was to use System.currentTimeMillis() which returns a time in milliseconds, however on some older operating systems, that timer has a poor resolution (granularity), which is the amount of time that must separate two timer calls. Windows XP has a resolution of 16ms, and Windows 98 even has a resolution of 55ms, meaning that no matter what, we would only be able to calculate a change in time greater than 55ms, and that differences in calls to System.currentTimeMillis() would be correct to 55ms. This can create some not so fluid animations. Usually we would want games to hit 60 noticable frames per second, as that's a common max refresh rate of monitors, and anything higher is unnoticeable (for most people including myself at least), so having a low timer resolution will help us create more frames that are noticably different. An alternative to System.currentTimeMillis(), is System.nanoTime(), a high resolution timer since Java 1.5 that returns a time in nanoseconds (note that the precision is in nanoseconds, not accuracy, at least, not yet). While this is probably excessive, it's nonetheless the most precise option available, so we might as well use it.
Another approach to updating animations is updating them by a tick based approach. An example of this approach is when you move a sprite a fixed amount of distance every time a certain piece of code pertaining to updating a position or animation is executed. Updating by how much time has elapsed is mostly superior to tick based animations, since with tick based animations, the speed of animations and movements of the sprite is never certain on any computer besides your own, as other computers can execute the code much more slowly or a lot faster, making some animations impossible to use, or very annoying to use. While tick based updating can help you do a few things with more ease (like detecting collisions), it is generally worth it to spend the time needed to program in time based updating.
// Just some set up variables to calculate FPS
long oldTime = System.nanoTime();
long nanoseconds = 0;
int frames = 0;
fps = 0;
// Just loop and loop forever, update state and then draw.
while (true)
{
// Relating to updating animations and calculating FPS
long elapsedTime = System.nanoTime() - oldTime;
oldTime = oldTime + elapsedTime;
nanoseconds = nanoseconds + elapsedTime;
frames++;
// Calculating a new fps value every second
if (nanoseconds >= 1000000000)
{
fps = frames;
nanoseconds = nanoseconds - 1000000000;
frames = 0;
}
// Update before we draw (because it makes more sense that way)
// (this method call updates all the sprites and animations
// based on how much time has elapsed)
update(elapsedTime);
...
}
Setting the speed of animations and sprites in pixels per nanoseconds will be quite a difficult task, simply because of how many zeroes there are. One trick is to set the speed in pixels per millisecond, and then in the constructor of an animation or whatever you use, convert the speed to nano seconds by dividing by 1,000,000.
public MovingCircle(float x, float y, int circleWidth,
int circleHeight, boolean down, boolean right, float speed)
{
this.x = x;
this.y = y;
this.circleWidth = circleWidth;
this.circleHeight = circleHeight;
this.down = down;
this.right = right;
// convert pixels per millisecond to nano second
// a lot easier to originally think about speeds in milliseconds
this.speed = speed / 1000000;
}
Easy locational painting, and painting to a stretchable BufferedImage
As we know, location 0,0 of an application is the top left corner of the application window, where the title bar is, but logically you would want this to be the top left corner of the screen between the window’s borders, especially after we went through with the hard work of correctly setting the window's dimension. To achieve this, we can simply create a BufferedImage with the same size as we want the screen to be, draw to that, then draw it on to the correct inside portion of the JFrame, and stretch it to fill the viewable area if needed. The following shows creation of a compatible BufferedImage that we will use throughout the program's execution.
// Create an image to draw to, instead of the graphics // object of the JFrame itself, because we can then stretch the image // over the JFrame when the user resizes. This allows the application's // graphics to grow or shrink with the initial size of the frame. Thus, // a larger resizes make's the application's graphics look bigger. // (Note that we are using the width and height from the constructor) drawing = GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice().getDefaultConfiguration() .createCompatibleImage(width, height);
The "compatible image", means that it's an image that can best support the display format that the application is running on. A compatible image allows Java 2D to do less work for us, and thus helping us achieve more frames. From the image, we can extract the graphics object from it, and then draw all of our graphics to it, starting with the background to erase what was previously there from the last update, and ending with our sprites (circles in this case). In addition, we can also set text and other graphics to be anti-aliased.
// Obtaining the graphics of our drawing image we use,
// we draw to this graphics object for the most part
Graphics2D drawingBoard = drawing.createGraphics();
// This allows our text and graphics to be nice and smooth
drawingBoard.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
drawingBoard.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Always draw over the image with a blank background, so we don't
// see the last frame's drawings! (comment this out and see what
// happens, it's fun pressing the change color button rapidly too!)
drawingBoard.setColor(Color.LIGHT_GRAY);
drawingBoard.fillRect(0, 0, drawing.getWidth(), drawing.getHeight());
// Now draw everything to drawingBoard, location 0,0 will be top left
// corner within the borders of the window
drawingBoard.setColor(circleColor);
for (MovingCircle circle : circles)
{
circle.draw(drawingBoard);
}
After all the graphics are painted to the image, we can then draw it to the JFrame in the correct location by using the Insets object again, and having it stretched or squeezed to fill the entire draw area of the JFrame.
// Now draw the drawing board to correct area of the JFrame's buffer // and stretch that image to fill the entire JFrame // NOTE: In this code example, we are doing this BEFORE we actively // render Swing. g.drawImage(drawing, insets.left, insets.top, this.getWidth() - (insets.left + insets.right), this.getHeight() - (insets.top + insets.bottom), null);
Note that we could have directly painted to the graphics object retrieved from the BufferStrategy, but in doing so, our graphics wouldn't get stretched or squeezed if we resized the frame larger or smaller. If we resized the frame smaller, some of our graphics would be drawn off screen, if we resized the frame larger, our graphics would just appear in the top left with a blank area to the bottom and right, or we might actually be showing more graphics of our application than we actually want to. Stretching the graphics ensures that we always show our graphics, and can always show our graphics using the most room that the user allows.
Actively rendering Swing components
There's a lot of talk from what I've seen and researched on the internet about how you should not use Swing in programs that use active rendering, and sometimes, how it's impossible to do so, and that you should create your own user interface components. However, it is possible, it's easy to do, and still runs fast. It may be true that creating and using your own light weight user interface might run your game a bit faster, but it isn't a reasonable thing to do. Most hobby games usually remain small enough that they will never reach the point in which Swing will slow them down. Rendering Swing actively also saves you an incredible amount of time from programming your own user interface. On a side note, you have the event dispatch thread running in the background taking care of everything related to Swing. So if don't clobber the thread with needless calculations, Swing will perform fast, be responsive, and not slow you down at all.
To get started, let's add some Swing components, as you would do so normally.
// Setting up the swing components
JPanel programTitlePanel = new JPanel(new FlowLayout());
programTitlePanel.add(new JLabel(
"Actively rendering graphics and Swing components together!"));
changeColor = new JButton("Change color");
changeColor.addActionListener(this);
JPanel changeColorPanel = new JPanel(new FlowLayout());
changeColorPanel.add(changeColor);
limitFps = new JButton("Unlimit FPS");
limitFps.addActionListener(this);
JPanel limitFpsPanel = new JPanel(new FlowLayout());
limitFpsPanel.add(limitFps);
JPanel holder = new JPanel(new GridLayout(2, 1)); // 2 rows, 1 column
holder.add(programTitlePanel);
holder.add(changeColorPanel);
add(BorderLayout.NORTH, holder);
add(BorderLayout.SOUTH, limitFpsPanel);
However, when we draw the Swing’s graphics after we draw our own graphics to the stretchable BufferedImage we use, the JFrame’s content pane’s background will paint over anything we painted. So let’s turn that off, along with other component's whose backgrounds we don't want shown. To explain it further using a JPanel as an example, a JPanel would fill up the entire screen with a gray background, but setting it to not be opaque means it's background won't show up (but its components will), and that we will see the graphics we drew previously underneath it.
// The JFrame's content pane's background will paint over any other // graphics we painted ourselves, so let's turn it transparent ((JComponent) getContentPane()).setOpaque(false); // Now set the JPanel's opaque, along with other Swing components whose // backgrounds we don't want shown changeColorPanel.setOpaque(false); programTitlePanel.setOpaque(false); limitFpsPanel.setOpaque(false); holder.setOpaque(false);
Next step is to draw the Swing components in our draw method, after we paint the rest of the frame's graphics. However, we do so by not painting the Swing components to our stretchable BufferedImage, but to the graphics object we retrieved from the BufferStrategy. Not doing so would paint the Swing components in incorrect positions, and incorrect sizes (Swing components don't usually resize on frame resizes, they just reposition). One last thing we do along with this, is to translate the graphics coordinate system to align with the inside portion of the JFrame where we draw to. This allows us to align the Swing components that we are rendering, with their clickable areas.
// Paint our Swing components, to the graphics object of the buffer, not // the BufferedImage being used for the application's sprites. // We do this, because Swing components don't resize on frame resizes, // they just reposition themselves, so we shouldn't stretch their // graphics. // Notice the translate, this is needed, to align our drawing of // components to their "clickable" areas (changes where 0,0 actually is) // (Comment it out and see what happens!) g.translate(insets.left, insets.top); getLayeredPane().paintComponents(g); // NOTE: make sure you do paint your own graphics first
Overriding the RepaintManager's functionality
There's still one thing left to do. We do not want any rendering to be called by Java on its own (since the rendering is already being done by us) so we have to cancel any request for a repainting. The RepaintManager class handles all repaint requests, so what we have to do is create our own RepaintManager with empty functionality in the method's we will override. In the main code example, we will simply override the whole class by creating a local inner class called NoRepaintManager.
/**
* NoRepaintManager is a RepaintManager that removes the functionality of
* the original RepaintManager for us so we don't have to worry about Java
* repainting on its own.
*/
class NoRepaintManager extends RepaintManager
{
public void addDirtyRegion(JComponent c, int x, int y, int w, int h){}
public void addInvalidComponent(JComponent invalidComponent){}
public void markCompletelyDirty(JComponent aComponent){}
public void paintDirtyRegions(){}
}
Then, in the constructor of our JFrame, we will write the following
// Set up our NoRepaintManager, this will eliminate any remaining // graphical glitches such as flickering when moving a mouse over a // button. RepaintManager repaintManager = new NoRepaintManager(); repaintManager.setDoubleBufferingEnabled(false); RepaintManager.setCurrentManager(repaintManager);
You are now seamlessly integrating Swing components in your actively rendering program.
Stopping the application from over using the processor
As you can imagine, rendering the graphics over and over as fast as you can in a loop means that the processor will do that work as fast as it can while using the majority of the processing power your computer has available. This behavior might not be something that you want to happen for a non-full screen game as a potential user may be multitasking with other programs they wish to use. However, with a call for the program to stop executing for a while, we can give the processor, for example, 10 milliseconds of rest every update. Note that this will now cap the FPS (frames per second), of the program to a max of 100 (1000 milliseconds in a second, divided by 10 milliseconds of rest per update = 100 updates max). This may not be the best method to cap a game at a certain FPS, but it's definitely an easy and quick fix.
if (limitingFPS)
{
// Sleep to let the processor handle other programs running,
// and to allow our game to not run needlessly fast.
// (It's quite easy to calculate how long to sleep
// to obtain a target FPS too, I'm just taking the
// lazy way out here though)
try
{
Thread.sleep(10);
}
catch (Exception e)
{
// ignore...
}
}
![]() |
![]() |
Creating the game loop
As we know, active rendering is when you constantly repaint the graphics of a program over and over again in a loop. Here's a game or update loop that would fit such a program.
/**
* Starts the game's mechanics up. Each iteration of the loop updates all
* animations and sprite locations and then draws the graphics of those
* animations and sprites.
*/
public void gameLoop()
{
// Just some set up variables to calculate FPS
long oldTime = System.nanoTime();
long nanoseconds = 0;
int frames = 0;
fps = 0;
// Just loop and loop forever, update state and then draw.
while (true)
{
// Relating to updating animations and calculating FPS
long elapsedTime = System.nanoTime() - oldTime;
oldTime = oldTime + elapsedTime;
nanoseconds = nanoseconds + elapsedTime;
frames++;
// Calculating a new fps value every second
if (nanoseconds >= 1000000000)
{
fps = frames;
nanoseconds = nanoseconds - 1000000000;
frames = 0;
}
// Update before we draw (because it makes more sense that way)
update(elapsedTime);
// Then after everything is updated, we can draw what we updated
Graphics2D g = null;
try
{
g = (Graphics2D) bufferStrategy.getDrawGraphics();
draw(g); // enter the method to draw everything
}
finally
{
g.dispose();
}
if (!bufferStrategy.contentsLost())
{
bufferStrategy.show();
}
// The sync call prevents possible event queue problems in Linux,
// I'm not sure if this call is needed anymore, it run's fine
// on my Linux machines going back to Ubuntu 8.04.
// In addition, this call is quite costly.
// Decomment it and see for yourself how much FPS drops.
// My 2.2ghz laptop running clocks this call taking between
// 4 to 5 milliseconds.
// Toolkit.getDefaultToolkit().sync();
if (limitingFPS)
{
// Sleep to let the processor handle other programs running,
// and to allow our game to not run needlessly fast.
// (It's quite easy to calculate how long to sleep
// to obtain a target FPS too, I'm just taking the
// lazy way out here though)
try
{
Thread.sleep(10);
}
catch (Exception e)
{
// ignore...
}
}
}
}
In the game loop, the loop calls the update(long elapsedTime) method. Here we update every object that depends on knowing how much time has elapsed.
/**
* Updates any objects that need to know how much time has elapsed to update
* animations and locations
*
* @param elapsedTime
* How much time has elapsed since the last update
*/
public void update(long elapsedTime)
{
for (MovingCircle circle : circles)
{
circle.update(elapsedTime);
}
}
The draw(Graphics2D g) method is also called. You've seen some of the code in this method already.
/**
* Draws the whole program, including all animations and Swing components
*
* @param g
* The program's window's graphics object to draw too
*/
public void draw(Graphics2D g)
{
// Obtaining the graphics of our drawing image we use,
// we draw to this graphics object for the most part
Graphics2D drawingBoard = drawing.createGraphics();
// This allows our text and graphics to be nice and smooth
drawingBoard.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
drawingBoard.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Always draw over the image with a blank background, so we don't
// see the last frame's drawings! (comment this out and see what
// happens, it's fun pressing the change color button rapidly too!)
drawingBoard.setColor(Color.LIGHT_GRAY);
drawingBoard.fillRect(0, 0, drawing.getWidth(), drawing.getHeight());
// Now draw everything to drawingBoard, location 0,0 will be top left
// corner within the borders of the window
drawingBoard.setColor(circleColor);
for (MovingCircle circle : circles)
{
circle.draw(drawingBoard);
}
// Now draw the drawing board to correct area of the JFrame's buffer
// and stretch that image to fill the entire JFrame
// NOTE: In this code example, we are doing this BEFORE we actively
// render Swing.
g.drawImage(drawing, insets.left, insets.top, this.getWidth()
- (insets.left + insets.right), this.getHeight()
- (insets.top + insets.bottom), null);
// Paint our Swing components, to the graphics object of the buffer, not
// the BufferedImage being used for the application's sprites.
// We do this, because Swing components don't resize on frame resizes,
// they just reposition themselves, so we shouldn't stretch their
// graphics.
// Notice the translate, this is needed, to align our drawing of
// components to their "clickable" areas (changes where 0,0 actually is)
// (Comment it out and see what happens!)
g.translate(insets.left, insets.top);
getLayeredPane().paintComponents(g);
// In addition, draw the FPS post stretch, so we always can read the fps
// even if you shrink the frame really small.
g.setColor(Color.WHITE);
// Grab the height to make sure we don't draw the FPS/UPS outside the
// draw area on accident
int fontHeight = g.getFontMetrics(this.getFont()).getHeight();
g.drawString("FPS/UPS: " + fps, 0, fontHeight);
drawingBoard.dispose();
}
Why I didn't use Canvas, and why you might want to
Why I didn't use Canvas
The main reason I didn't use a Canvas is that it's not a container. Since a Canvas is not a container, it means we cannot place Swing components on top of where we drew those graphics using just the Canvas itself. You can however place Swing components on top of the Canvas, if you used the Canvas and a JPanel full of components together with a JLayeredPane. Send me an email if you would like to see a program done using that method.
If we only used a Canvas without the JLayeredPane method, we would be forced to place our user interface to the side of the Canvas, or, we would have to implement our own user interface library using MouseListeners and MouseMotionListeners. To illustrate, I've created two pictures on the difference between how nice it is to have your Swing components on top of your graphics, the following is using one of the two ideal methods:
![]() |
Using only a Canvas without a JLayeredPane, we won't be able to add Swing components to it, thus we'll have to add the canvas and Swing components to different areas of the JFrame.
![]() |
This is a poor example though, for such a basic game with no animations, it may make more sense to just skip active rendering and use a combination of JButtons, JLabels, and ImageIcons to achieve what the first mockup shows.
Canvas advantages
The advantage of a Canvas is that you can add it to a JFrame, and a JApplet. This allows you possibly have one class that has a Canvas, and two classes that can deploy your application as an Applet or Frame. Similarly, for a passive rendering application, your common class could be a JPanel. Personally, I would just use Java Web Start instead of deploying an Applet, and that's why I'm setting up a BufferStrategy on the JFrame itself.
Summary
Main advantages: Fastest way to render your program, allows for consistent FPS for constant smooth animations, and helps with drawing Swing components over, or under, your application or game's graphics (you can technically do this with passive rendering too, just use a JLayeredPane, with a background layer, interface layer, and top most layer).
Main disadvantages: Complicated and lots of code. For a more simple approach, check out my passive rendering guide, Double Buffering and Passive Rendering in Java using the a Timer Object, passive rendering can achieve similar FPS results, with less of a headache. Passive rendering is also naturally multithreaded.
Program example
Here's a program example I wrote that uses all the techniques I mentioned above.
/**
* ActiveCircles.java, an example of active rendering while double buffering
* The article on this source code can be found at:
* http://jamesgames.org/resources/double_buffer/double_buffering_and_active_rendering.html
* Code demonstrates: - Active rendering in swing in a resizable frame
* - properly set width and height of a JFrame using Insets
* - double buffering and active rendering via BufferStrategy
* - usage of a high resolution timer for time based animations
* - stretching an application's graphics with resizes
* - actively rendering Swing components
* @author James Murphy
* @version 06/15/2012, original: 04/16/10
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Random;
/**
* See above.
*
* @author James Murphy
*/
public class ActiveCircles extends JFrame implements ActionListener
{
// Used to randomize circle positions and colors
private Random random;
// List of our sprites
private MovingCircle[] circles;
// Manages the buffering of the program
private BufferStrategy bufferStrategy;
// Set true to limit fps (sleep the thread), false to not
private boolean limitingFPS;
// Button to randomize circle colors
private JButton changeColor;
// Button to switch the value of limitingFPS
private JButton limitFps;
// The color of the current circle, ideally, we would probably want this
// part of the Circle class, keeping it here for simplicity.
private Color circleColor;
// Holds the latest calculated value of frames per second
private int fps;
// We draw to this image always, then stretch it over the entire frame.
// This allows a resize to make the game bigger, as opposed to
// just providing a larger area for the sprites to be on.
// We also are using this image's width and height to define
// the coordinate system for the circles to stay on. This help's us
// obtain our goal of stretching the game's graphics on resizes.
private BufferedImage drawing;
// Only an instance variable just because we call this so many times. Note
// this may be problematic if your insets change over the course of the
// application while still referring to the initial insets. (Like if you go
// to fullscreen mode where insets disappear)
private Insets insets;
/**
* @param args
* Contains nothing for this program.
*/
public static void main(String[] args)
{
ActiveCircles activeCirclesExample = new ActiveCircles(50,
700, 500);
activeCirclesExample.gameLoop();
}
/**
* Constructor for ActiveCircles
*
* @param numberOfCircles
* The number of circles you want the program to display
* @param width
* The width of the program's inside portion of the frame
* @param height
* The height of the program's inside portion of the frame
*/
public ActiveCircles(int numberOfCircles, int width, int height)
{
super();
setTitle("Active rendering with Swing and double buffering circles");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setIgnoreRepaint(true); // don't need Java painting for us
// Set up our NoRepaintManager, this will eliminate any remaining
// graphical glitches such as flickering when moving a mouse over a
// button.
RepaintManager repaintManager = new NoRepaintManager();
repaintManager.setDoubleBufferingEnabled(false);
RepaintManager.setCurrentManager(repaintManager);
// Correct change width and height of window so that the available
// screen space actually corresponds to what is passed, another
// method is the Canvas object + pack()
setSize(width, height);
insets = this.getInsets();
int insetWide = insets.left + insets.right;
int insetTall = insets.top + insets.bottom;
setSize(getWidth() + insetWide, getHeight() + insetTall);
// Setting up the bouncing circles
circleColor = Color.DARK_GRAY;
circles = new MovingCircle[numberOfCircles];
random = new Random();
int circleWidth = 50;
int circleHeight = 50;
float maxSpeed = .5f;
for (int i = 0; i < circles.length; i++)
{
circles[i] = new MovingCircle(random.nextFloat()
* (getWidth() - circleWidth), random.nextFloat()
* (getHeight() - circleHeight), circleWidth, circleHeight,
random.nextBoolean(), random.nextBoolean(), random
.nextFloat()
* maxSpeed);
}
// Setting up the swing components
JPanel programTitlePanel = new JPanel(new FlowLayout());
programTitlePanel.add(new JLabel(
"Actively rendering graphics and Swing components together!"));
changeColor = new JButton("Change color");
changeColor.addActionListener(this);
JPanel changeColorPanel = new JPanel(new FlowLayout());
changeColorPanel.add(changeColor);
limitFps = new JButton("Unlimit FPS");
limitFps.addActionListener(this);
JPanel limitFpsPanel = new JPanel(new FlowLayout());
limitFpsPanel.add(limitFps);
JPanel holder = new JPanel(new GridLayout(2, 1)); // 2 rows, 1 column
holder.add(programTitlePanel);
holder.add(changeColorPanel);
add(BorderLayout.NORTH, holder);
add(BorderLayout.SOUTH, limitFpsPanel);
// The JFrame's content pane's background will paint over any other
// graphics we painted ourselves, so let's turn it transparent
((JComponent) getContentPane()).setOpaque(false);
// Now set the JPanel's opaque, along with other Swing components whose
// backgrounds we don't want shown
changeColorPanel.setOpaque(false);
programTitlePanel.setOpaque(false);
limitFpsPanel.setOpaque(false);
holder.setOpaque(false);
limitingFPS = true;
// Create a buffer strategy using two buffers
createBufferStrategy(2);
// Keeping a reference of the strategy is handy
bufferStrategy = getBufferStrategy();
// Create an image to draw to, instead of the graphics
// object of the JFrame itself, because we can then stretch the image
// over the JFrame when the user resizes. This allows the application's
// graphics to grow or shrink with the initial size of the frame. Thus,
// a larger resizes make's the application's graphics look bigger.
// (Note that we are using the width and height from the constructor)
drawing = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration()
.createCompatibleImage(width, height);
}
/**
* Starts the game's mechanics up. Each iteration of the loop updates all
* animations and sprite locations and then draws the graphics of those
* animations and sprites.
*/
public void gameLoop()
{
// Just some set up variables to calculate FPS
long oldTime = System.nanoTime();
long nanoseconds = 0;
int frames = 0;
fps = 0;
// Just loop and loop forever, update state and then draw.
while (true)
{
// Relating to updating animations and calculating FPS
long elapsedTime = System.nanoTime() - oldTime;
oldTime = oldTime + elapsedTime;
nanoseconds = nanoseconds + elapsedTime;
frames++;
// Calculating a new fps value every second
if (nanoseconds >= 1000000000)
{
fps = frames;
nanoseconds = nanoseconds - 1000000000;
frames = 0;
}
// Update before we draw (because it makes more sense that way)
update(elapsedTime);
// Then after everything is updated, we can draw what we updated
Graphics2D g = null;
try
{
g = (Graphics2D) bufferStrategy.getDrawGraphics();
draw(g); // enter the method to draw everything
}
finally
{
g.dispose();
}
if (!bufferStrategy.contentsLost())
{
bufferStrategy.show();
}
// The sync call prevents possible event queue problems in Linux,
// I'm not sure if this call is needed anymore, it run's fine
// on my Linux machines going back to Ubuntu 8.04.
// In addition, this call is quite costly.
// Decomment it and see for yourself how much FPS drops.
// My 2.2ghz laptop running clocks this call taking between
// 4 to 5 milliseconds.
// Toolkit.getDefaultToolkit().sync();
if (limitingFPS)
{
// Sleep to let the processor handle other programs running,
// and to allow our game to not run needlessly fast.
// (It's quite easy to calculate how long to sleep
// to obtain a target FPS too, I'm just taking the
// lazy way out here though)
try
{
Thread.sleep(10);
}
catch (Exception e)
{
// ignore...
}
}
}
}
/**
* Updates any objects that need to know how much time has elapsed to update
* animations and locations
*
* @param elapsedTime
* How much time has elapsed since the last update
*/
public void update(long elapsedTime)
{
for (MovingCircle circle : circles)
{
circle.update(elapsedTime);
}
}
/**
* Draws the whole program, including all animations and Swing components
*
* @param g
* The program's window's graphics object to draw too
*/
public void draw(Graphics2D g)
{
// Obtaining the graphics of our drawing image we use,
// we draw to this graphics object for the most part
Graphics2D drawingBoard = drawing.createGraphics();
// This allows our text and graphics to be nice and smooth
drawingBoard.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
drawingBoard.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Always draw over the image with a blank background, so we don't
// see the last frame's drawings! (comment this out and see what
// happens, it's fun pressing the change color button rapidly too!)
drawingBoard.setColor(Color.LIGHT_GRAY);
drawingBoard.fillRect(0, 0, drawing.getWidth(), drawing.getHeight());
// Now draw everything to drawingBoard, location 0,0 will be top left
// corner within the borders of the window
drawingBoard.setColor(circleColor);
for (MovingCircle circle : circles)
{
circle.draw(drawingBoard);
}
// Now draw the drawing board to correct area of the JFrame's buffer
// and stretch that image to fill the entire JFrame
// NOTE: In this code example, we are doing this BEFORE we actively
// render Swing.
g.drawImage(drawing, insets.left, insets.top, this.getWidth()
- (insets.left + insets.right), this.getHeight()
- (insets.top + insets.bottom), null);
// Paint our Swing components, to the graphics object of the buffer, not
// the BufferedImage being used for the application's sprites.
// We do this, because Swing components don't resize on frame resizes,
// they just reposition themselves, so we shouldn't stretch their
// graphics.
// Notice the translate, this is needed, to align our drawing of
// components to their "clickable" areas (changes where 0,0 actually is)
// (Comment it out and see what happens!)
g.translate(insets.left, insets.top);
getLayeredPane().paintComponents(g);
// In addition, draw the FPS post stretch, so we always can read the fps
// even if you shrink the frame really small.
g.setColor(Color.WHITE);
// Grab the height to make sure we don't draw the FPS/UPS outside the
// draw area on accident
int fontHeight = g.getFontMetrics(this.getFont()).getHeight();
g.drawString("FPS/UPS: " + fps, 0, fontHeight);
drawingBoard.dispose();
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == changeColor)
{
circleColor = new Color(random.nextInt(256), random.nextInt(256),
random.nextInt(256));
}
if (e.getSource() == limitFps)
{
limitingFPS = !limitingFPS;
if (limitingFPS)
{
limitFps.setText("Unlimit FPS");
}
else
{
limitFps.setText("Limit FPS");
}
}
}
/**
* A moving circle is a circle that moves around the screen bouncing off
* walls
*
* @author James Murphy
*/
class MovingCircle
{
private float x;
private float y;
private int circleWidth;
private int circleHeight;
private boolean down;
private boolean right;
private float speed; // pixels per nanosecond
public MovingCircle(float x, float y, int circleWidth,
int circleHeight, boolean down, boolean right, float speed)
{
this.x = x;
this.y = y;
this.circleWidth = circleWidth;
this.circleHeight = circleHeight;
this.down = down;
this.right = right;
// convert pixels per millisecond to nano second
// a lot easier to originally think about speeds in milliseconds
this.speed = speed / 1000000;
}
/**
* Update the circle, which for now is moving the circle, and detecting
* collisions.
*
* @param elapsedTime
* The time that has elapsed since the last time the circle
* was updated.
*/
public void update(long elapsedTime)
{
float pixelMovement = elapsedTime * speed;
if (down)
{
y = y + pixelMovement;
}
else
{
y = y - pixelMovement;
}
if (right)
{
x = x + pixelMovement;
}
else
{
x = x - pixelMovement;
}
// test if circle hit a side of the window
// move the circle off the wall also to prevent collision sticking
if (y < 0)
{
down = !down;
y = 0;
}
if (y > drawing.getHeight() - circleHeight)
{
down = !down;
y = drawing.getHeight() - circleHeight;
}
if (x < 0)
{
right = !right;
x = 0;
}
if (x > drawing.getWidth() - circleWidth)
{
right = !right;
x = drawing.getWidth() - circleWidth;
}
}
/**
* Draw the circle
*
* @param g
* Graphics object to draw to
*/
public void draw(Graphics g)
{
g.fillOval((int) x, (int) y, circleWidth, circleHeight);
}
}
/**
* NoRepaintManager is a RepaintManager that removes the functionality of
* the original RepaintManager for us so we don't have to worry about Java
* repainting on it's own.
*/
class NoRepaintManager extends RepaintManager
{
public void addDirtyRegion(JComponent c, int x, int y, int w, int h){}
public void addInvalidComponent(JComponent invalidComponent){}
public void markCompletelyDirty(JComponent aComponent){}
public void paintDirtyRegions(){}
}
}




