How to speed up a simple paintComponent()

F

fxn

I am quite new to Swing programming. I wrote a Swing component which is
a matrix of small rectangles, following the guidelines in the
documentation online.

Each cell of the matrix can have a background color and a list of
Drawables, which are objects who live in a cell and typically display
themselves just via setColor + fillRect or something like that. Nothing
fancy.

The whole matrix is changed very often (it is not like a chessboard for
instance) since it is the display of some type of discrete simulations,
and the code I have so far is:

public class Grid extends JComponent {
private Cell[][] cells;
private Set cellsToPaint;
// ...

public Grid() {
setOpaque(true);
// ...
}

protected synchronized void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());

cellWidth = getWidth()/xcells;
cellHeight = getHeight()/ycells;

Iterator iter = cellsToPaint.iterator(); // subset of cells to paint
while (iter.hasNext()) {
Point p = (Point) iter.next();
Cell c = cells[p.x][p.y];
if (c.drawables.isEmpty() && c.bgcolor == null)
iter.remove(); // cache maintenance
else
paintCell(p.x, p.y, c, g);
}
}

private void paintCell(int x, int y, Cell c, Graphics g) {
int[] cr = getCellRectangle(x, y);
if (c.bgcolor != null && !c.bgcolor.equals(getBackground())) {
g.setColor(c.bgcolor);
g.fillRect(cr[0], cr[1], cr[2], cr[3]);
}
Iterator iter = c.drawables.iterator();
while (iter.hasNext()) {
// we pass a copy of the graphics object
// to ensure the original one remains untouched
// as the Swing tutorial recommends
Graphics2D g2 = (Graphics2D) g.create();
((Drawable) iter.next()).draw(cr, g2);
g2.dispose();
}
}
// ...
}

This implementation works, but based on some comparisons looks like it
is clear that it is slow. That looked like the natural way to write
that, but it Is there some alternative Swing technique to speed this up
fundamentally?

-- fxn
 
J

John

I am quite new to Swing programming. I wrote a Swing component which is
a matrix of small rectangles, following the guidelines in the
documentation online.

Each cell of the matrix can have a background color and a list of
Drawables, which are objects who live in a cell and typically display
themselves just via setColor + fillRect or something like that. Nothing
fancy.

The whole matrix is changed very often (it is not like a chessboard for
instance) since it is the display of some type of discrete simulations,
and the code I have so far is:

public class Grid extends JComponent {
private Cell[][] cells;
private Set cellsToPaint;
// ...

public Grid() {
setOpaque(true);
// ...
}

protected synchronized void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());

cellWidth = getWidth()/xcells;
cellHeight = getHeight()/ycells;

Iterator iter = cellsToPaint.iterator(); // subset of cells to paint
while (iter.hasNext()) {
Point p = (Point) iter.next();
Cell c = cells[p.x][p.y];
if (c.drawables.isEmpty() && c.bgcolor == null)
iter.remove(); // cache maintenance
else
paintCell(p.x, p.y, c, g);
}
}

private void paintCell(int x, int y, Cell c, Graphics g) {
int[] cr = getCellRectangle(x, y);
if (c.bgcolor != null && !c.bgcolor.equals(getBackground())) {
g.setColor(c.bgcolor);
g.fillRect(cr[0], cr[1], cr[2], cr[3]);
}
Iterator iter = c.drawables.iterator();
while (iter.hasNext()) {
// we pass a copy of the graphics object
// to ensure the original one remains untouched
// as the Swing tutorial recommends
Graphics2D g2 = (Graphics2D) g.create();
((Drawable) iter.next()).draw(cr, g2);
g2.dispose();
}
}
// ...
}

This implementation works, but based on some comparisons looks like it
is clear that it is slow. That looked like the natural way to write
that, but it Is there some alternative Swing technique to speed this up
fundamentally?

-- fxn

In terms of speed, I think you would be fastest using 1 component for
your matrix.

I would try Canvas (awt) or Panel (swing) and simply draw directly onto it.

You would obviously just work out which part of the component is each
cell and redraw it as required.

--that's my fast suggestion, but it could be quite messy and isn't very OO.

If I had to achieve what you are doing, I would use a GridLayout and add
small panels/labels. You could then set the background colours to do
what you need to. In a real-world example on decent hardware and non
massive grids, this would work fine.

John
 
R

Remi Bastide

I am quite new to Swing programming. I wrote a Swing component which is ....

private void paintCell(int x, int y, Cell c, Graphics g) {
int[] cr = getCellRectangle(x, y);
if (c.bgcolor != null && !c.bgcolor.equals(getBackground())) {
g.setColor(c.bgcolor);
g.fillRect(cr[0], cr[1], cr[2], cr[3]);
}
Iterator iter = c.drawables.iterator();
while (iter.hasNext()) {
// we pass a copy of the graphics object
// to ensure the original one remains untouched
// as the Swing tutorial recommends
Graphics2D g2 = (Graphics2D) g.create();
((Drawable) iter.next()).draw(cr, g2);
g2.dispose();
}
}
// ...
}

This implementation works, but based on some comparisons looks like it
is clear that it is slow. That looked like the natural way to write
that, but it Is there some alternative Swing technique to speed this up
fundamentally?

Maybe the fact that you create a new Graphics for each Drawable is
overkill. If you are in control of the code in Drawable.draw, you can
make sure that the graphics parameter is restored in its original
state, which should be faster than creating a new one for each call on
draw().

Also remembre that you can pass a parameter to repaint() stating the
"dirty area" to be repainted, and you can access this information when
repainting by asking the RepaintManager. You could exploit this to
repaint only the cells that are in the dirty area, but I'm not sure
this would provide much improvement (the test needed to find out if
the cell is inside the dirty area might take as long a the code to
paint the cell anyway, especially if painting it is just a matter of
filling a rectangle).
 
F

fxn

Removing the creation of the Graphics2D didn't make a significant
difference, thank you!

I saw some people using BuuferedImages behind the scenes, but I am not
fluent with that kind of components and the underlying performance
implications. Would it be better to paint onto a BufferedImage on each
step and dump the image onto the component or something in that line?

-- fxn
 
J

John C. Bollinger

Removing the creation of the Graphics2D didn't make a significant
difference, thank you!

Since you're thanking him so emphatically, I suppose you mean that it
_did_ make significant difference.
I saw some people using BuuferedImages behind the scenes, but I am not
fluent with that kind of components and the underlying performance
implications. Would it be better to paint onto a BufferedImage on each
step and dump the image onto the component or something in that line?

You are observing code that performs its own double buffering. The
drawing code writes to a BufferedImage, then when it's done it displays
displays the image on the component. This reduces flicker, but as far
as I know it does not improve the performance of the drawing code.
Swing components do this for you unless you turn double buffering off.
 
F

fxn

Since you're thanking him so emphatically, I suppose you mean that it
_did_ make significant difference.

Unfortunately it didn't, I guess whatever I need to improve is
relatively far more heavier than the creation of that object. I thanked
him for the suggestion, I didn't know that was expensive so that will
actually improve the code.

-- fxn
 
C

Chris Uppal

The whole matrix is changed very often (it is not like a chessboard for
instance) since it is the display of some type of discrete simulations,
[...]

Assuming that youv'e already taken obvious steps like only updating cells that
are actually visible (not scrolled out of sight), and that your graphics code
doesn't contain hidden bugs or misfeatures that slow it down dramatically, then
the chances are that this is a mismatch between the amount of computation that
is needed by one step of the simulation, verses that needed by updating the
display.

I.e. you are burning most of your runtiime trying to make the display keep up
with the simulation.

If that's the case, then I suggest decoupling the two. For the very simplest
technique, only update the display every, say, 1000 times around the simulation
loop. More complex schemes are possible. In one thing of my own, I made the
simulation thread produce a "snapshot" of its state every <so many> times
around its main loop (it didn't know about the GUI at all), and made the GUI
ask for the latest "snapshot" about once a second, and update its display from
that. That had the advantage that the simulation thread didn't have to
synchronise access to its real runtime data, but could go ahead at maximum
speed, and just snapshot itself (a simple copy) at very long (by its
standards) intervals.

-- chris
 

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

Forum statistics

Threads
474,261
Messages
2,571,040
Members
48,769
Latest member
Clifft

Latest Threads

Top