paint(); How to avoid flickering?

S

Scott Steiner

hi,

I'm writing a backgammon game which involves drawing the board on a
JPanel and drawing the checkers on the board and have the user move them
around per drag and drop. The drawing contents of the JPanel are scaled
accordingly as the JPanel is resized.

All is fine so far except that there is much flickering as a checker is
dragged over the board. I assume this is because the whole board is
repainted every time during the drag and drop process of a checker. The
simplified code which involves drawing the board and drawing and moving
just one checker basically looks like this:


public class BoardPanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2D = (Graphics2D)g;
g2D.scale(scaleX,scaleY);

//here come a bunch of g2D.fillRect and g2D.fillPolygon methods
//that draw the board

//this code draws the checker in its new position
g2D.setColor(new Color(234,204,174));
checker = new Ellipse2D.Double();
checker.setFrame(posX, posY, 60, 60);
g2D.fill(checker);
}

public void componentResized(ComponentEvent arg0)
{
//when the panel is resized then its contents are resized automatically
scaleX = (double)this.getWidth() / 1152;
scaleY = (double)this.getHeight() / 839;
this.repaint();
}

public void mouseDragged(MouseEvent e)
{
if (pressedIn) //pressedIn is just a boolean that is set true in
//the mousePressed event if user clicks on a checker
{
updateLocation(e);
}
}

public void updateLocation(MouseEvent e)
{
...
posX = newXposition;
posY = newYposition;
this.repaint();
}
}

Can someone help me adjust the above code so that no flickering is seen?

Many thx!
 
T

Thomas G. Marshall

Scott Steiner coughed up:
hi,

I'm writing a backgammon game which involves drawing the board on a
JPanel and drawing the checkers on the board and have the user move
them around per drag and drop. The drawing contents of the JPanel are
scaled accordingly as the JPanel is resized.

All is fine so far except that there is much flickering as a checker
is dragged over the board. I assume this is because the whole board is
repainted every time during the drag and drop process of a checker.
The simplified code which involves drawing the board and drawing and
moving just one checker basically looks like this:


public class BoardPanel extends JPanel
{
public void paint(Graphics g)

Stop right there.

If you are intent on using a lightweight component like the swing JPanel,
then design the thing entirely using swing facilities.

Use paintComponent(). Swing JComponents are designed to use double
buffering by default. If that doesn't solve your flicker problem, then
report back. Double buffering is a standard graphics technique---it'd be
worth looking up on the off chance that you are unfamiliar with it.

Good luck---report back.
 
S

Scott Steiner

Thomas G. Marshall said:
Scott Steiner coughed up:

Stop right there.

If you are intent on using a lightweight component like the swing JPanel,
then design the thing entirely using swing facilities.

Use paintComponent(). Swing JComponents are designed to use double
buffering by default. If that doesn't solve your flicker problem, then
report back. Double buffering is a standard graphics technique---it'd be
worth looking up on the off chance that you are unfamiliar with it.

Good luck---report back.
[...]

hi Thomas,

thanks for your prompt reply. Here are my results:

1) using paintComponent() instead of paint() didn't solve my flickering
problem, unless there was more to change in my code other than rename my
paint() method to paintComponent().

2) I tried a repaint(x,y,width,height) instead of repaint() in my method
that is called in the mouseDragged event. For the values x,y,width and
height I used an area around the dragged checker which worked fine and
cleaned the trailing dirt the checker leaves behind when it is dragged
and avoids flickering since I'm not repainting the entire JPanel.
However, if the checker is dragged very fast, then I do get a bit of a
dirt trail because the cleaning area is not big enough anymore to clear
the dirt of the last position which is now further away as the checker
was moved very fast.

3) In fact I looked up on double buffering prior to my post but could
never get any of the sample code I found on the internet to fit in my
code and work. I realize I must have understood something wrong.
Hopefully you could help me out here on what exactly I must do.

Thanks again.
 
T

Thomas G. Marshall

Scott Steiner coughed up:
Thomas G. Marshall said:
Scott Steiner coughed up:

Stop right there.

If you are intent on using a lightweight component like the swing
JPanel, then design the thing entirely using swing facilities.

Use paintComponent(). Swing JComponents are designed to use double
buffering by default. If that doesn't solve your flicker problem,
then report back. Double buffering is a standard graphics
technique---it'd be worth looking up on the off chance that you are
unfamiliar with it.

Good luck---report back.
[...]

hi Thomas,

thanks for your prompt reply. Here are my results:

1) using paintComponent() instead of paint() didn't solve my
flickering problem, unless there was more to change in my code other
than rename my paint() method to paintComponent().

2) I tried a repaint(x,y,width,height) instead of repaint() in my
method that is called in the mouseDragged event. For the values
x,y,width and height I used an area around the dragged checker which
worked fine and cleaned the trailing dirt the checker leaves behind
when it is dragged and avoids flickering since I'm not repainting the
entire JPanel. However, if the checker is dragged very fast, then I
do get a bit of a dirt trail because the cleaning area is not big
enough anymore to clear the dirt of the last position which is now
further away as the checker was moved very fast.

3) In fact I looked up on double buffering prior to my post but could
never get any of the sample code I found on the internet to fit in my
code and work. I realize I must have understood something wrong.
Hopefully you could help me out here on what exactly I must do.

Thanks again.


Can you create the smallest possible code that is cut and pasteable that
exhibits that problem? That "simplified code" you posted has undeclared
members in it. It would do you well to do this, as you might just discover
that you've done it right and there is no solution, though I don't think so.

Come to think of it, I remember a flicker problem that I couldn't get rid of
through the normal means, but that was e-ONS ago, in the awt pre-swing days
ugly days of 1.0 IIRC. I remember it suddenly vanished from one rev. to the
other, and my assumption was that the copying to the screen wasn't
previously done at the most primitive level, between frame refreshes. But
again, I cannot remember.

Thanks!
 
S

Scott Steiner

Thomas G. Marshall said:
Scott Steiner coughed up:
Thomas G. Marshall said:
Scott Steiner coughed up:
hi,

I'm writing a backgammon game which involves drawing the board on a
JPanel and drawing the checkers on the board and have the user move
them around per drag and drop. The drawing contents of the JPanel
are scaled accordingly as the JPanel is resized.

All is fine so far except that there is much flickering as a checker
is dragged over the board. I assume this is because the whole board
is repainted every time during the drag and drop process of a
checker. The simplified code which involves drawing the board and
drawing and moving just one checker basically looks like this:


public class BoardPanel extends JPanel
{
public void paint(Graphics g)

Stop right there.

If you are intent on using a lightweight component like the swing
JPanel, then design the thing entirely using swing facilities.

Use paintComponent(). Swing JComponents are designed to use double
buffering by default. If that doesn't solve your flicker problem,
then report back. Double buffering is a standard graphics
technique---it'd be worth looking up on the off chance that you are
unfamiliar with it.

Good luck---report back.
[...]

hi Thomas,

thanks for your prompt reply. Here are my results:

1) using paintComponent() instead of paint() didn't solve my
flickering problem, unless there was more to change in my code other
than rename my paint() method to paintComponent().

2) I tried a repaint(x,y,width,height) instead of repaint() in my
method that is called in the mouseDragged event. For the values
x,y,width and height I used an area around the dragged checker which
worked fine and cleaned the trailing dirt the checker leaves behind
when it is dragged and avoids flickering since I'm not repainting the
entire JPanel. However, if the checker is dragged very fast, then I
do get a bit of a dirt trail because the cleaning area is not big
enough anymore to clear the dirt of the last position which is now
further away as the checker was moved very fast.

3) In fact I looked up on double buffering prior to my post but could
never get any of the sample code I found on the internet to fit in my
code and work. I realize I must have understood something wrong.
Hopefully you could help me out here on what exactly I must do.

Thanks again.

Can you create the smallest possible code that is cut and pasteable that
exhibits that problem? That "simplified code" you posted has undeclared
members in it. It would do you well to do this, as you might just discover
that you've done it right and there is no solution, though I don't think so.

I have pasted the code, not much really, the class where all the work is
done and a small starter class, that's 2 classes in all.

When you run the starter class then the board should display and you
should see a white round checker in the top right corner. You can now
drag the checker around. Also note that the application functions at any
window size i.e. the user can resize the window at any time.

Just note that the flicker is gone when I use repaint(x,y,w,h) instead
of paint() in method updateLocation(), however, if you drag the checker
very fast you will see some dirt left.

Many thanks!

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

public class BoardPanel extends JPanel implements ComponentListener, MouseListener, MouseMotionListener
{
private Ellipse2D.Double checker;
private double scaleX = 1;
private double scaleY = 1;
private int posX = 986;
private int posY = 31;
private int clickedX, clickedY;
private int frameX, frameY;
private int deltaX = 0;
private int deltaY = 0;
private boolean pressedIn = false;

public BoardPanel()
{
this.addComponentListener(this);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}


public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;

g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2D.scale(scaleX,scaleY);

g2D.setColor(new Color(80,40,0)); //brown border left
g2D.fill3DRect(0,0,576,839,true);

g2D.setColor(new Color(44,109,24)); //green left
g2D.fillRect(100,30,432,779);

g2D.setColor(new Color(80,40,0)); //brown border right
g2D.fill3DRect(576,0,576,839,true);

g2D.setColor(new Color(44,109,24)); //green right
g2D.fillRect(620,30,432,779);

g2D.setColor(new Color(0,39,0)); //ditch left
g2D.fillRect(20,30,60,779);

g2D.setColor(new Color(0,39,0)); //ditch right
g2D.fillRect(1072,30,60,779);

g2D.setColor(new Color(80,40,0)); //brown border ditch seperator left
g2D.fill3DRect(20,359,60,121,true);

g2D.setColor(new Color(80,40,0)); //brown border ditch seperator right
g2D.fill3DRect(1072,359,60,121,true);

for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(25,25,25)); //point black
else
g2D.setColor(new Color(109,0,0)); //point red

int[] x = {100 + (72*(i-1)),136 + (72*(i-1)),172 + (72*(i-1))};
int[] y = {30,359,30};
g2D.fillPolygon(x,y,3);
}

for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(25,25,25)); //point black
else
g2D.setColor(new Color(109,0,0)); //point red

int[] x = {620 + (72*(i-1)),656 + (72*(i-1)),692 + (72*(i-1))};
int[] y = {30,359,30};
g2D.fillPolygon(x,y,3);
}

for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(109,0,0)); //point red
else
g2D.setColor(new Color(25,25,25)); //point black

int[] x = {100 + (72*(i-1)),136 + (72*(i-1)),172 + (72*(i-1))};
int[] y = {809,480,809};
g2D.fillPolygon(x,y,3);
}

for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(109,0,0)); //point red
else
g2D.setColor(new Color(25,25,25)); //point black

int[] x = {620 + (72*(i-1)),656 + (72*(i-1)),692 + (72*(i-1))};
int[] y = {809,480,809};
g2D.fillPolygon(x,y,3);
}

//single white checker
g2D.setColor(new Color(234,204,174));
checker = new Ellipse2D.Double();
checker.setFrame(posX, posY, 60, 60);
g2D.fill(checker);
}

public void componentShown(ComponentEvent arg0) {}
public void componentHidden(ComponentEvent arg0) {}
public void componentMoved(ComponentEvent arg0) {}
public void componentResized(ComponentEvent arg0)
{
scaleX = (double)this.getWidth() / 1152;
scaleY = (double)this.getHeight() / 839;
this.repaint();
}

public void mousePressed(MouseEvent e)
{
if (this.checker.contains((e.getX()/(double)this.getWidth())*1152, (e.getY()/(double)this.getHeight())*839))
{
Cursor hand_cursor = new Cursor(Cursor.HAND_CURSOR);
this.setCursor(hand_cursor);
pressedIn = true;
Rectangle2D r = checker.getFrame();
frameX = (int)r.getX();
frameY = (int)r.getY();
clickedX = e.getX();
clickedY = e.getY();
}
else
{
pressedIn = false;
}
}

public void mouseReleased(MouseEvent e)
{
Cursor default_cursor = new Cursor(Cursor.DEFAULT_CURSOR);
this.setCursor(default_cursor);
}

public void mouseClicked(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}

public void mouseDragged(MouseEvent e)
{
if (pressedIn)
{
updateLocation(e);
}
}

public void mouseMoved(MouseEvent e){}

public void updateLocation(MouseEvent e)
{
deltaX = (int)(((e.getX() - clickedX)/(double)this.getWidth())*1152);
deltaY = (int)(((e.getY() - clickedY)/(double)this.getHeight())*839);
posX = frameX + deltaX;
posY = frameY + deltaY;
//only repaint small area around current point to avoid repaint of whole panel which leads to flicker
this.repaint(e.getX()-60,e.getY()-60,120,120);
}
}

//---------------------------------------------------------------------------------------
//Starter class
//---------------------------------------------------------------------------------------

import javax.swing.*;
import java.awt.*;

public class Test extends JFrame
{
public Test()
{
this.setSize(Toolkit.getDefaultToolkit().getScreenSize());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = this.getContentPane();
c.setLayout(new BorderLayout());

BoardPanel p = new BoardPanel();
c.add(p);

this.setVisible(true);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}

public static void main(String[] args)
{
new Test();
}
}
 
R

Ryan Dillon

Trying overriding the update(Graphics g) method to call paint(g) only.
The default implementation clears the background which may be causing
the flickering.

Cheers
 
J

John B. Matthews

Scott Steiner said:
Just note that the flicker is gone when I use repaint(x,y,w,h) instead
of paint() in method updateLocation(), however, if you drag the checker
very fast you will see some dirt left. [...]
public void mouseReleased(MouseEvent e)
{ this.repaint();
Cursor default_cursor = new Cursor(Cursor.DEFAULT_CURSOR);
this.setCursor(default_cursor);
} [...]
public void updateLocation(MouseEvent e)
{ this.repaint(posX-90,posY-90,150,150);
deltaX = (int)(((e.getX() - clickedX)/(double)this.getWidth())*1152);
deltaY = (int)(((e.getY() - clickedY)/(double)this.getHeight())*839);
posX = frameX + deltaX;
posY = frameY + deltaY;
//only repaint small area around current point
this.repaint(e.getX()-60,e.getY()-60,120,120);
}
}
[...]

One approach: In updateLocation, add the old stone to the update
region before updating its position by calling repaint() with a
suitably scaled rectangle. Also, add this.repaint() to
mouseReleased() in order to clean up stray bits when the mouse is
released.

Alternatively: Simplify paintComponent so that a plain repaint() can
keep up in updateLocation.
 
S

Scott Steiner

John B. Matthews said:
Scott Steiner said:
Just note that the flicker is gone when I use repaint(x,y,w,h) instead
of paint() in method updateLocation(), however, if you drag the checker
very fast you will see some dirt left. [...]
public void mouseReleased(MouseEvent e)
{ this.repaint();
Cursor default_cursor = new Cursor(Cursor.DEFAULT_CURSOR);
this.setCursor(default_cursor);
} [...]
public void updateLocation(MouseEvent e)
{ this.repaint(posX-90,posY-90,150,150);
deltaX = (int)(((e.getX() - clickedX)/(double)this.getWidth())*1152);
deltaY = (int)(((e.getY() - clickedY)/(double)this.getHeight())*839);
posX = frameX + deltaX;
posY = frameY + deltaY;
//only repaint small area around current point
this.repaint(e.getX()-60,e.getY()-60,120,120);
}
}
[...]

One approach: In updateLocation, add the old stone to the update
region before updating its position by calling repaint() with a
suitably scaled rectangle. Also, add this.repaint() to
mouseReleased() in order to clean up stray bits when the mouse is
released.
[...]

Excellent tip! Thanks John!

I repainted the old checker region like you suggested and it worked. At
first it didn't work but I realized that I needed to scale posX and posY
accordingly in addition and then it worked. Also the repaint() in
mouseReleased is a very good idea too!

Thanks again, I appreciate your help!
 
S

Scott Steiner

Ryan said:
Trying overriding the update(Graphics g) method to call paint(g) only.
The default implementation clears the background which may be causing
the flickering.

That was the first thing I tried out but I found out that my
update(Graphics g) wasn't being called at all. I searched the internet
and I read in some forum that there is no guarantee that update() will
alaways be called by repaint(), which matched my observation.
 
T

Thomas G. Marshall

Scott Steiner coughed up:
That was the first thing I tried out but I found out that my
update(Graphics g) wasn't being called at all. I searched the internet
and I read in some forum that there is no guarantee that update() will
alaways be called by repaint(), which matched my observation.


In /all cases/ repaint() only sets a flag to signal that a new painting
should occur. It does not "call" the paint (paintComponent) methods
directly. This is necessary, particularly since you want to be able to call
repaint() multiple times in multiple places without having to worry about a
full painting occurring each time.



--
Enough is enough. It is /not/ a requirement that someone must google
relentlessly for an answer before posting in usenet. Newsgroups are
for discussions. Discussions do /not/ necessitate prior research. If
you are bothered by someone asking a question without taking time to
look something up, simply do not respond.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top