I am following the intermediate java tutorial found at
https://www.youtube.com/watch?v=M2GoiOas2u8&list=PL54DB126285ED0420 .
But I am having issues with the double buffering flickering.
my relevant code in two of my classes is as follows
GamePanel.Java
private void gameRender()
{
if (dbImage == null) // create the buffer
{
dbImage = createImage(GWIDTH, GHEIGHT);
}
if (dbImage == null)
{
System.err.println("dbImage is still null!");
return;
}
else
{
dbg = dbImage.getGraphics();
}
// Clear the screen
dbg.setColor(Color.WHITE);
dbg.fillRect(0, 0, GWIDTH, GHEIGHT);
// Draw Game elements
draw(dbg);
}
// Draw all game content in this method
public void draw(Graphics g)
{
world.draw(g);
}
private void paintScreen()
{
Graphics g;
try
{
g = this.getGraphics();
if (dbImage != null && g != null)
{
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync(); //For some OS.
g.dispose();
}
catch(Exception e)
{
System.err.println(e);
}
}
World.Java
public void draw(Graphics g)
{
for (int i = 0; i < arrayNumber; i++)
{
g.drawImage(blockImg[i], blocks[i].x, blocks[i].y, null);
}
}
Where arrayNumber = 1200.
This program only has flickering issues when the map is moved to the right or down, (meaning that blocks.x and blocks.y increase for each value of i), but not the other way around. I tried drawing from the bottom right up by replacing each [i] with [arrayNumber - i] and modifying the parameters of the for loop. This made the flickering only occur when moving the map up and to the left.
So I know that I should somehow modify this run method in the world class to somehow use double buffering, but was wondering how I would go about doing so since world doesn't extend JPanel?
Don't use this.getGraphics();, this is not how painting in Swing is done.
Swing uses a passive painting algorithm, which means you do not control the painting process. Swing will make decisions about what and when something should be painted.
Swing components are already double buffered by default.
Instead of fighting this, you should understand and embrace it.
Start by overriding paintComponent of you GamePanel and render your game state here (don't forget to call super.paintComponent)
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting is achieved in Swing
Related
I am trying to make an animation by Java but having trouble understanding the DoubleBufferImage and DoubleBufferGraphics. I understand about the update(), render(), and paint() sequences. However, In the methods of each, I can't understand how it is being drawn. Here are the codes.
gameUpdate()
// I will just skip the updating part because I first need to understand the background.
gameRender()
private void gameRender() {
if (DoubleBufferImage == null) {
System.out.println("The Image is null: Error occurence");
DoubleBufferImage = createImage(P_WIDTH - 15, P_HEIGHT - 15);
}
DoubleBufferGraphic = DoubleBufferImage.getGraphics();
DoubleBufferGraphic.setColor(Color.LIGHT_GRAY);
DoubleBufferGraphic.fillRect(0, 0, P_WIDTH, P_HEIGHT);
DoubleBufferGraphic.setColor(Color.BLUE);
DoubleBufferGraphic.setFont(font);
DoubleBufferGraphic.drawString("Average FPS/UPS: " +
df.format(averageFPS) + df.format(averageUPS), 10, 10);
}
paint()
public void paintScreen() {
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (DoubleBufferImage != null)) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
}
There's also paintComponent method which is overrided from Jpanel.
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (DoubleBufferImage != null) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
}
Here's my question. The running goes as update -> render -> and painting
However as you can see from the render, they used graphics to draw. But is it all drawn in DoubleBufferImage? Is Image instance similar to panel instance? I mean, is it just addable to the frame?
As this is the Double Buffering system, I want to know which methods is drawing directly, and which one is the method that draws beforehand.
Finally in the running, there is no code that it's the Image or Graphics that we made are going to be added to the panel. I just want to know the timing that the BufferedImage is being drawn.
Please help! I can't upload the whole code so I'm not sure if u guys can understand :(
Okay, so you seem to be banging against some misinformation and misunderstandings. Perhaps you should start by having a look at something like paintComponent() vs paint() and JPanel vs Canvas in a paintbrush-type GUI
In AWT/Swing there are at least two ways of performing custom painting, each with there pros and cons.
You could use paintComponent, which a "hook" into the painting system used by Swing. Swing makes use of a "passive" rendering system. This means you don't have control of when something get's painted, the paint system makes those decisions and then calls (indirectly) the paintComponent of your component so you can perform the updates.
The other mechanism (BufferStrategy) uses a "active" rendering system, which gives you complete control over when painting occurs.
As a general rule, you can't mix them. Swing has it's own painting system and won't play well with BufferStrategy, so that means if you want to use Swing components as part of your output, you can't.
But that won't answer your question, or not directly
Let's try and break it down
gameRender
private void gameRender() {
if (DoubleBufferImage == null) {
System.out.println("The Image is null: Error occurence");
DoubleBufferImage = createImage(P_WIDTH - 15, P_HEIGHT - 15);
}
DoubleBufferGraphic = DoubleBufferImage.getGraphics();
DoubleBufferGraphic.setColor(Color.LIGHT_GRAY);
DoubleBufferGraphic.fillRect(0, 0, P_WIDTH, P_HEIGHT);
DoubleBufferGraphic.setColor(Color.BLUE);
DoubleBufferGraphic.setFont(font);
DoubleBufferGraphic.drawString("Average FPS/UPS: " +
df.format(averageFPS) + df.format(averageUPS), 10, 10);
}
At this moment in time, DoubleBufferImage seems to be a BufferedImage, so when gameRender is called, it checks to see if a buffer exists and creates it as needed. It then takes a reference of the DoubleBufferImages Graphics context and prepares it for rendering, cleaning off what was previously painted to it.
A quick overview of Graphics
Graphics is an abstract layer over the top of the underlying rendering pipeline, often implement using either OpenGL or DirectX depending on the system. It provides a common layer onto which graphics operations can be performed in a system independent manner
paintScreen
public void paintScreen() {
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (DoubleBufferImage != null)) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
}
This worries me, as I have no context, but this.getGraphics() seems to be taking a reference of a component's Graphics context and drawing the DoubleBufferImage to it.
This is dangerous and ill advised. getGraphics returns a snapshot of the component from when it was last painted, which could be painted over at anytime when the component is painted again.
You should, at the earliest opportunity, get rid of this method.
paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (DoubleBufferImage != null) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
}
As mentioned above, paintComponent is the preferred method for hooking into the paint system of Swing. All this does is paints the DoubleBufferImage it, although it should actually read g.drawImage(DoubleBufferImage, 0, 0, this);
So, what is all this doing?
Basically, it's an ill-advised attempt to perform double buffering in Swing ... which is already double buffered by default.
However as you can see from the render, they used graphics to draw. But is it all drawn in DoubleBufferImage? Is Image instance similar to panel instance?
No. A component is an object, which has a number of properties. One of it's jobs is to paint it's state, which is done via the various paint methods, which is passed a Graphics context which is attached to a native peer and eventually will be rendered to the screen (or printer).
A BufferedImage is just that, an image. The Graphics context is just a simple means by which you can paint to it. It can then be saved to a file or, as is the case here, painted to a component (via it's Graphics) context.
As I said above, Graphics is just an abstract layer, which allows you to perform painting operations to a number of different destinations, screen, printers, images, etc...
I mean, is it just addable to the frame?
No. It's not a component based class
As this is the Double Buffering system, I want to know which methods is drawing directly, and which one is the method that draws beforehand.
paintComponent and (the ill-advised) paintScreen are painting the image directly to the component, which will, eventually be rendered to the screen by the painting sub system.
Further reading...
Performing Custom Painting
Painting in AWT and Swing
Passive vs. Active Rendering
BufferStrategy and BufferCapabilities
I'm currently trying to draw an image to a JFrame (just a nonsense test image). In the code bite below, the image is drawn to the JFrame, but the area around the image that doesn't fill JFrame is black that rapidly flashes.
Here is the code below:
try {
myImage = ImageIO.read(ImagesMain.class.getResource("/Textures/TestImage.png"));
}catch(Exception e) {
e.printStackTrace();
}
BufferStrategy strategy = null;
while(strategy == null) {//I know this is terrible practice, just doing this way because its inside main
strategy = myCanvas.getBufferStrategy();
if(myCanvas.getBufferStrategy() == null) {
myCanvas.createBufferStrategy(3);
}
}
myFrame.setVisible(true);
//Rendering part
while(true) {
do {
do {
g = strategy.getDrawGraphics();
g.setColor(Color.WHITE);
g.drawImage(myImage, 20, 20, null);
g.dispose();
}while(strategy.contentsRestored());
strategy.show();
}while(strategy.contentsLost());
}
I've tested and retested my code several times to no avail. I should also add that this is all done in the main method (for testing purposes). Long story short, how do I display my image without the unnecessary black flashing around the image?
When this happens, it is because one is not clearing the Frame to which they are drawing. In this instance,
g.clearRect(x, y, height, width); is needed to clear the drawing frame and display a clear image.
Answer courtesy of #MadProgrammer above, in comments.
Long story short I am drawing the Mandelbrot using a BufferedImage that I put in a custom JPanel. I have already done the zooming in the set but have problems with repainting when unzooming. When unzooming I change the value of the image to that of the previous state of the image(I keep every state in a Stack) and repaint the panel. Problem is that the last image in the stack gets popped off but it is not drawn.
These are my instance variables
private BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
private Stack<BufferedImage> zooms = new Stack<BufferedImage>();
private boolean unzoom = false;
This is how I zoom and push the image that I want to save on a stack
public void mouseReleased(MouseEvent e)
{
zooms.push(image);
<some code for zooming that works>
repaint();
}
Now I want to unzoom by scrolling
class WheelZoomListener implements MouseWheelListener
{
public void mouseWheelMoved(MouseWheelEvent e)
{
unzoom = true;
//this is how I assign the current image to be the one before the last zoom
image = zooms.pop();
repaint();
}
}
Finally this is my paint method
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d= (Graphics2D) g;
//if it is not unzooming draw the mandelbrot set normally by
//dealing with every pixel of the Buffered Image separately
if (!unzoom)
{
for(int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
int iterations = getIterations(cnSet[i][j]);
if (iterations == noIterations)
{
color = Color.BLACK;
}
else
{
color = cols[iterations%noIterations];
}
image.setRGB(i, j, color.getRGB());
}
}
}
//zooming or unzooming always draw the image in its current state
g2d.drawImage(image, 0, 0, this);
unzoom = false;
}
FIX: It turned out that I don't need to keep the last image and create temporary images every time. Instead now I only keep the coordinates of the complex plane in a stack. That is all I need to repaint the old image again.
This:
private BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
Instantiates a new BufferedImage and stores a reference to that object in image.
This:
zooms.push(image);
Pushes the reference to that single BufferedImage you created onto the stack.
As long as you keep using the same BufferedImage, all you are doing is pushing multiple references to the same object onto the stack; so changes to the object's data are reflected in every reference you've placed on the stack, because every item in the stack points to the same object.
The high-level effect is you are changing every previous state to the current one every time you render.
You'll want to create a whole new BufferedImage for each state; so that each reference you stick on the stack points to a unique object.
Take a look at this nice little article about how references work in Java.
I'm doing the following to a Canvas object.
graphics.setColor(BLUE);
graphics.fill(new Rectangle2D.Double(x, y, width, height));
I'd like to fade in the fill colour to create a smooth transition from the canvas background colour to the new colour (and possibly fade out whatever colour was originally there).
I've played with this kind of thing (setting the graphics object's composite to an AlphaComposite which a Timer updating the alpha value every n milliseconds) but I get flickering.
I'm wondering what general concept I'm missing.
Thanks for any pointers.
First of all, how could you be using the AWT? It is quite outdated. I reccomend you switch to swing, mainly because swing has double buffering, which would remove your flicker.
Your application does exactly what you tell it to do. If you want to make a fade-in effect, you have to determine what kind of color changes you want to make, create a function which does it, and implement the fade itself.
I'd approach it like that:
class FadeEffect{
int totalDurationMs;
int elapsedDurationMs;
Color initialColor;
Color finalColor;
Color getColor(int durationDelta) {
elapsedDurationMs += durationDelta;
if (elapsedDurationMs > totalDurationMs) {
return finalColor;
}
double progress = 1.0d*elapsedDurationMs/totalDurationMs;
return new Color( (int)(finalColor.getRed()-initialColor.getRed())*progress,
(int)(finalColor.getGreen()-initialColor.getGreen())*progress,
(int)(finalColor.getBlue()-initialColor.getBlue())*progress);
}
//getters, setters, etc
}
As for the flickering issue: make sure you are using double buffering - either in your component, or by manually drawing on a off-screen buffer (image) and only posting the image to the screen when the drawing is complete.
Here is a sample code from my Graphic2D app doing the double buffering:
private VolatileImage vImg;
#Override
public void paint(Graphics g) {
if (gc==null) gc = this.getGraphicsConfiguration();
do {
boolean sizeChanged = false;
sizeChanged = (vImg!=null&&(vImg.getWidth()!=getWidth()|| vImg.getHeight()!=getHeight()));
if (vImg == null || vImg.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE
|| sizeChanged) {
vImg = gc.createCompatibleVolatileImage(getWidth(), getHeight());
vImg.setAccelerationPriority(1);
}
final Graphics gimg = vImg.getGraphics();
if (gimg instanceof Graphics2D) {
renderContents((Graphics2D) gimg);
gimg.dispose();
g.drawImage(vImg, 0, 0, null);
} else {
throw new UnsupportedOperationException("Rendering impossible, graphics are not of Graphics2D class");
}
} while (vImg.contentsLost());
updateAnimationNo();
}
EDIT TWO
To prevent snarky comments and one-line answers missing the point: IFF it is as simple as calling setDoubleBuffered(true), then how do I get access to the current offline buffer so that I can start messing with the BufferedImage's underlying pixel databuffer?
I took the time to write a running piece of code (which looks kinda fun too) so I'd really appreciate answers actually answering (what a shock ;) my question and explaining what/how this is working instead of one-liners and snarky comments ;)
Here's a working piece of code that bounces a square across a JFrame. I'd like to know about the various ways that can be used to transform this piece of code so that it uses double-buffering.
Note that the way I clear the screen and redraw the square ain't the most efficient but this is really not what this question is about (in a way, it's better for the sake of this example that it is somewhat slow).
Basically, I need to constantly modify a lot pixels in a BufferedImage (as to have some kind of animation) and I don't want to see the visual artifacts due to single-buffering on screen.
I've got a JLabel whose Icon is an ImageIcon wrapping a BufferedImage. I want to modify that BufferedImage.
What has to be done so that this becomes double-buffered?
I understand that somehow "image 1" will be shown while I'll be drawing on "image 2". But then once I'm done drawing on "image 2", how do I "quickly" replace "image 1" by "image 2"?
Is this something I should be doing manually, like, say, by swapping the JLabel's ImageIcon myself?
Should I be always drawing in the same BufferedImage then do a fast 'blit' of that BufferedImage's pixels in the JLabel's ImageIcon's BufferedImage? (I guess no and I don't see how I could "synch" this with the monitor's "vertical blank line" [or equivalent in flat-screen: I mean, to 'synch' without interfering with the moment the monitor itselfs refreshes its pixels, as to prevent shearing]).
What about the "repaint" orders? Am I suppose to trigger these myself? Which/when exactly should I call repaint() or something else?
The most important requirement is that I should be modifying pixels directly in the images's pixel databuffer.
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class DemosDoubleBuffering extends JFrame {
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
int xs = 3;
int ys = xs;
int x = 0;
int y = 0;
final int r = 80;
final BufferedImage bi1;
public static void main( final String[] args ) {
final DemosDoubleBuffering frame = new DemosDoubleBuffering();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing( WindowEvent e) {
System.exit(0);
}
});
frame.setSize( WIDTH, HEIGHT );
frame.pack();
frame.setVisible( true );
}
public DemosDoubleBuffering() {
super( "Trying to do double buffering" );
final JLabel jl = new JLabel();
bi1 = new BufferedImage( WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB );
final Thread t = new Thread( new Runnable() {
public void run() {
while ( true ) {
move();
drawSquare( bi1 );
jl.repaint();
try {Thread.sleep(10);} catch (InterruptedException e) {}
}
}
});
t.start();
jl.setIcon( new ImageIcon( bi1 ) );
getContentPane().add( jl );
}
private void drawSquare( final BufferedImage bi ) {
final int[] buf = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
for (int i = 0; i < buf.length; i++) {
buf[i] = 0xFFFFFFFF; // clearing all white
}
for (int xx = 0; xx < r; xx++) {
for (int yy = 0; yy < r; yy++) {
buf[WIDTH*(yy+y)+xx+x] = 0xFF000000;
}
}
}
private void move() {
if ( !(x + xs >= 0 && x + xs + r < bi1.getWidth()) ) {
xs = -xs;
}
if ( !(y + ys >= 0 && y + ys + r < bi1.getHeight()) ) {
ys = -ys;
}
x += xs;
y += ys;
}
}
EDIT
This is not for a full-screen Java application, but a regular Java application, running in its own (somewhat small) window.
---- Edited to address per pixel setting ----
The item blow addresses double buffering, but there's also an issue on how to get pixels into a BufferedImage.
If you call
WriteableRaster raster = bi.getRaster()
on the BufferedImage it will return a WriteableRaster. From there you can use
int[] pixels = new int[WIDTH*HEIGHT];
// code to set array elements here
raster.setPixel(0, 0, pixels);
Note that you would probably want to optimize the code to not actually create a new array for each rendering. In addition, you would probably want to optimized the array clearing code to not use a for loop.
Arrays.fill(pixels, 0xFFFFFFFF);
would probably outperform your loop setting the background to white.
---- Edited after response ----
The key is in your original setup of the JFrame and inside the run rendering loop.
First you need to tell SWING to stop Rasterizing whenever it wants to; because, you'll be telling it when you're done drawing to the buffered image you want to swap out in full. Do this with JFrame's
setIgnoreRepaint(true);
Then you'll want to create a buffer strategy. Basically it specifies how many buffers you want to use
createBufferStrategy(2);
Now that you tried to create the buffer strategy, you need to grab the BufferStrategy object as you will need it later to switch buffers.
final BufferStrategy bufferStrategy = getBufferStrategy();
Inside your Thread modify the run() loop to contain:
...
move();
drawSqure(bi1);
Graphics g = bufferStrategy.getDrawGraphics();
g.drawImage(bi1, 0, 0, null);
g.dispose();
bufferStrategy.show();
...
The graphics grabbed from the bufferStrategy will be the off-screen Graphics object, when creating triple buffering, it will be the "next" off-screen Graphics object in a round-robin fashion.
The image and the Graphics context are not related in a containment scenario, and you told Swing you'd do the drawing yourself, so you have to draw the image manually. This is not always a bad thing, as you can specify the buffer flipping when the image is fully drawn (and not before).
Disposing of the graphics object is just a good idea as it helps in garbage collection. Showing the bufferStrategy will flip buffers.
While there might have been a misstep somewhere in the above code, this should get you 90% of the way there. Good luck!
---- Original post follows ----
It might seem silly to refer such a question to a javase tutorial, but have you looked into BufferStrategy and BufferCapatbilites?
The main issue I think you are encountering is that you are fooled by the name of the Image. A BufferedImage has nothing to do with double buffering, it has to do with "buffering the data (typically from disk) in memory." As such, you will need two BufferedImages if you wish to have a "double buffered image"; as it is unwise to alter pixels in image which is being shown (it might cause repainting issues).
In your rendering code, you grab the graphics object. If you set up double buffering according to the tutorial above, this means you will grab (by default) the off-screen Graphics object, and all drawing will be off-screen. Then you draw your image (the right one of course) to the off-screen object. Finally, you tell the strategy to show() the buffer, and it will do the replacement of the Graphics context for you.
Generally we use Canvas class which is suitable for animation in Java. Anyhoo, following is how you achieve double buffering:
class CustomCanvas extends Canvas {
private Image dbImage;
private Graphics dbg;
int x_pos, y_pos;
public CustomCanvas () {
}
public void update (Graphics g) {
// initialize buffer
if (dbImage == null) {
dbImage = createImage (this.getSize().width, this.getSize().height);
dbg = dbImage.getGraphics ();
}
// clear screen in background
dbg.setColor (getBackground ());
dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);
// draw elements in background
dbg.setColor (getForeground());
paint (dbg);
// draw image on the screen
g.drawImage (dbImage, 0, 0, this);
}
public void paint (Graphics g)
{
g.setColor (Color.red);
g.fillOval (x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
}
}
Now you can update the x_pos and y_pos from a thread, followed by the 'repaint' call on the canvas object. The same technique should work on a JPanel as well.
What you want is basically impossible in windowed mode with Swing. There is no support for raster synchronization for window repaints, this is only available in fullscreen mode (and even then may not be supported by all platforms).
Swing components are double-buffered by default, that is they will do all the rendering to an intermediate buffer and that buffer is then finally copied to the screen, avoiding flicker from background clearing and then painting on top of it.
And thats the only strategy that is reasonable well supported on all underlying platforms. It avoids only repaint flickering, but not visual tearing from moving graphic elements.
A reasonably simple way of having access to the raw pixels of an area fully under you control would be to extend a custom component from JComponent and overwrite its paintComponent()-method to paint the area from a BufferedImage (from memory):
public class PixelBufferComponent extends JComponent {
private BufferedImage bufferImage;
public PixelBufferComponent(int width, int height) {
bufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
setPreferredSize(new Dimension(width, height));
}
public void paintComponent(Graphics g) {
g.drawImage(bufferImage, 0, 0, null);
}
}
You can then manipulate you buffered image whichever way you desire. To get your changes made visible on screen, simply call repaint() on it. If you do the pixel manipulation from a thread other than the EDT, you need TWO buffered images to cope with race conditions between the actual repaint and your manipulation thread.
Note that this skeleton will not paint the entire area of the component when used with a layout manager that stretches the component beyond its preferred size.
Note also, the buffered image approach mostly only makes sense if you do real low level pixel manipulation via setRGB(...) on the image or if you directly access the underlying DataBuffer directly. If you can do all the manipulations using Graphics2D's methods, you could do all the stuff in the paintComponent method using the provided graphics (which is actually a Graphics2D and can be simply casted).
Here's a variation in which all drawing takes place on the event dispatch thread.
Addendum:
Basically, I need to constantly modify a lot pixels in a BufferedImage…
This kinetic model illustrates several approaches to pixel animation.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.image.BufferedImage;
/** #see http://stackoverflow.com/questions/4430356 */
public class DemosDoubleBuffering extends JPanel implements ActionListener {
private static final int W = 600;
private static final int H = 400;
private static final int r = 80;
private int xs = 3;
private int ys = xs;
private int x = 0;
private int y = 0;
private final BufferedImage bi;
private final JLabel jl = new JLabel();
private final Timer t = new Timer(10, this);
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DemosDoubleBuffering());
frame.pack();
frame.setVisible(true);
}
});
}
public DemosDoubleBuffering() {
super(true);
this.setLayout(new GridLayout());
this.setPreferredSize(new Dimension(W, H));
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
jl.setIcon(new ImageIcon(bi));
this.add(jl);
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
move();
drawSquare(bi);
jl.repaint();
}
private void drawSquare(final BufferedImage bi) {
Graphics2D g = bi.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, W, H);
g.setColor(Color.blue);
g.fillRect(x, y, r, r);
g.dispose();
}
private void move() {
if (!(x + xs >= 0 && x + xs + r < bi.getWidth())) {
xs = -xs;
}
if (!(y + ys >= 0 && y + ys + r < bi.getHeight())) {
ys = -ys;
}
x += xs;
y += ys;
}
}