Using JComponent.getGraphics() in Swing for incremental painting  
Author Message
mcarrato





PostPosted: 2004-2-13 15:48:00 Top

java-programmer, Using JComponent.getGraphics() in Swing for incremental painting Hello,

I have read many posts and articles saying "don't ever use
getGraphics() in Swing JComponents... always use paintComponent()!" I
understand the motivation for such statements: the paintComponent()
method should be used to implement painting, in order to ensure
correct behavior in all situations.

However, my question is: is it OK, from within an event handler, to
paint using getGraphics() PURELY AS AN OPTIMIZATION, AS LONG AS
paintComponent() does the right thing?

An example:

class MyComponent extends JComponent
{
ArrayList thingsToPaint=new ArrayList();
public void mySwingEventHandler(SomeEvent e)
{
//update thingsToPaint so that paintComponent
//works correctly
thingsToPaint.add(somethingNewToPaint);
//try painting directly
Graphics g = getGraphics();
if(g != null)
{
//we have a graphics, so paint the new
//thing directly.
somethingNewToPaint.drawOn(g);
g.dispose();
}
else
{
//No graphics object available... so just
//call repaint().
repaint();
}
}
public void paintComponent(Graphics g)
{
//paint everything
int i, size = thingsToPaint.size();
for(i=0; i < size; ++i)
{
((ThingToPaint) thingsToPaint.get(i)).drawOn(g);
}
}
};

I actually have a good reason for doing this. I am writing a simple
image manipulation program, and I need to implement a rectanglar
selection facility. In order to accomplish this, I need to listen to
mouseDragged events and repaint the rectangle on the image at each new
position. Swing forces you to re-paint the entire image each time
(i.e. no incremental updates), which is annoyingly slow for this
particular problem. And I don't want to turn off double buffering.

If I use getGraphics() directly (as in the example above), I can just
update a tiny section of the image, and it is blindingly fast and
smooth. I have tested this, and it works wonderfully in my limited
tests. But I don't want to do it if it will be problematic down the
line.

So, is it OK to use getGraphics() if the full repainting functionality
is available in paintComponent()? Or is there some other reason not to
use getGraphics()?

Thanks,

Mike Carrato
 
Thomas Weidenfeller





PostPosted: 2004-2-13 16:37:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting Michael Carrato wrote:
> However, my question is: is it OK, from within an event handler, to
> paint using getGraphics() PURELY AS AN OPTIMIZATION, AS LONG AS
> paintComponent() does the right thing?

In general, you already have everything for painting this new part in
paintComponent(), so why duplicate code?

In your case, where you have a need for immediate action (e.g. when you
handle some cross-hair cursor, selection or some interactive resizing),
have a look at paintImmediately().

If you would have added just another shape, etc. to the drawing,
consider just calling repaint() with the affected region as parameter.
Especially if you have no need for immediate feedback.

In both cases consider to make sure that you observe the cliping area
inside paintComponent(), so you don't paint more than necessary.
Observing the cliping region might be a much better optimization if you
have many things to paint, and if you have managed to organize them in a
data structure which allows for efficient spatial separation. Otherwise
Swing will have to do the cliping for each item individually. If you
don't have many things to paint, you can leave the entire cliping to
Swing, it isn't too bad at it.

/Thomas

 
mcarrato





PostPosted: 2004-2-16 23:24:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting Thomas Weidenfeller <email***@***.com> wrote in message news:<c0i28h$3jk$email***@***.com>...
> Michael Carrato wrote:
> > However, my question is: is it OK, from within an event handler, to
> > paint using getGraphics() PURELY AS AN OPTIMIZATION, AS LONG AS
> > paintComponent() does the right thing?
>
> ...
>
> In your case, where you have a need for immediate action (e.g. when you
> handle some cross-hair cursor, selection or some interactive resizing),
> have a look at paintImmediately().
>

I didn't realize that paintImmediately() can be called with a
rectangle. I've since tried that, and performance is surprisingly
good. Swing does a really nice job of optimizing painting, even within
a single component.

So now I'm sold on paintImmediately(), but I still would like to
suppress the double buffering overhead, if possible. Double buffering
causes the entire screen image to be copied into the offscreen image
buffer, and for something as interactive as this I'd like to do as
little redrawing as possible.

Can I de-activate double buffering temporarily while calling
paintImmediately(), then re-activate it after I'm done?

> If you would have added just another shape, etc. to the drawing,
> consider just calling repaint() with the affected region as parameter.
> Especially if you have no need for immediate feedback.

Yes, there is a need for immediate feedback. It's a rectangular region
selector, so each mouse movement event requires a repaint of the
selection rectangle.

>
> In both cases consider to make sure that you observe the cliping area
> inside paintComponent(), so you don't paint more than necessary.
> Observing the cliping region might be a much better optimization if you
> have many things to paint, and if you have managed to organize them in a
> data structure which allows for efficient spatial separation. Otherwise
> Swing will have to do the cliping for each item individually. If you
> don't have many things to paint, you can leave the entire cliping to
> Swing, it isn't too bad at it.

These are all great tips. Thank you for your time.

>
> /Thomas


Mike Carrato
 
 
ak





PostPosted: 2004-2-17 0:16:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting > Can I de-activate double buffering temporarily while calling
> paintImmediately(), then re-activate it after I'm done?
>
JComponent#setDoubleBuffered(true);

____________

http://reader.imagero.com the best java image reader.


 
 
mcarrato





PostPosted: 2004-2-17 23:35:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting "ak" <email***@***.com> wrote in message news:<c0qqbt$ict$email***@***.com>...
> > Can I de-activate double buffering temporarily while calling
> > paintImmediately(), then re-activate it after I'm done?
> >
> JComponent#setDoubleBuffered(true);

Yes, but:
(1) Can I switch just before paintImmediately() and switch back just
after?
(2) Do I call setDoubleBuffered? on the JComponent itself, or do I
need to call it on the enclosing frame?

Mike
 
 
Thomas Weidenfeller





PostPosted: 2004-2-17 23:55:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting Michael Carrato wrote:
> Yes, but:
> (1) Can I switch just before paintImmediately() and switch back just
> after?
> (2) Do I call setDoubleBuffered? on the JComponent itself, or do I
> need to call it on the enclosing frame?

http://java.sun.com/products/jfc/tsc/articles/painting/index.html#db

and

http://java.sun.com/products/jfc/tsc/articles/painting/index.html#paint_process

should get you started.

/Thomas

 
 
mcarrato





PostPosted: 2004-2-18 5:21:00 Top

java-programmer >> Using JComponent.getGraphics() in Swing for incremental painting Thomas Weidenfeller <email***@***.com> wrote in message news:<c0tddm$ioo$email***@***.com>...
> Michael Carrato wrote:
> > Yes, but:
> > (1) Can I switch just before paintImmediately() and switch back just
> > after?
> > (2) Do I call setDoubleBuffered? on the JComponent itself, or do I
> > need to call it on the enclosing frame?
>
> http://java.sun.com/products/jfc/tsc/articles/painting/index.html#db
>
> and
>
> http://java.sun.com/products/jfc/tsc/articles/painting/index.html#paint_process
>
> should get you started.
>
> /Thomas

Thanks for the links. I had read this article a few days ago, but I
was still stuck on the getGraphics()-based solution, so I forgot about
it.

So, based on everything I've read, I am planning to try the following
in my event handler:

boolean isDB =
theJComponent.getRootPane().isDoubleBuffered();
//turn off double buffering, temporarily
theJComponent.getRootPane().setDoubleBuffered(false);
paintImmediately(...);
//restore the original buffering setting.
theJComponent.getRootPane().setDoubleBuffered(isDB);

Thanks again for your help.

Mike Carrato