avoid clearing the screen in JComponent

Hi,

I want to draw a custom JComponent (a waveform-display) with a “marker” indicating actual play position,the drawing is performed by an overidden paintComponent(). For performance-reasons i just wanna draw the area around the marker. This works just fine, but since the whole screen is deleted every frame, I just see this small area. I tried setOpaque(false) which should do the trick of not clearing the screen… but this didn’t help.
The “old” method of overwriting update(Graphics g) wasn’t successful,either.
The JComponent itself is situated on a JPanel.
Anyone here with a smart suggestion :slight_smile: ?

greetings

basti

[quote=“lebasti,post:1,topic:25253”]
Every frame? What do you mean by “every frame” in the context of Swing repainting? I.e. how are you triggering repaints?

Are you calling repaint()? If so, perhaps you can call the version that takes a clipping region. You will always need to paint everything in the “dirty” region, that’s how Swing works. So just optimize your waveform play-head updating so that it asks only to repaint the region that really changed.

You can also try caching the actual waveform portion in an off-screen image, then drawing the playhead on top of it. So if the wave data doesn’t change you can just blit the existing waveform image, then the play-head, instead of going through the wave data and drawing tons of lines.

Scott

Thanks for the immediate answer.
repaint() is called whenever something in the waveform changed, e.g. widht, height of the window or the region of the waveform to display. Because I’m doing the whole redraw-logic in my paintComponent() (which includes automatic detection of the area which needs a redraw) I want to override the whole swing-stuff. Your idea with image and the blit is nice, but if possible i would prefer my method. I remember this worked in earlier java-versions. I’ll continue investigations,

greetings

basti

Ok, I just tried your second suggestion (since all my other approaches haven’t been successful). It basically works, but it’s god-damn slow… maybe I am doing something significantly wrong:


if(redrawAll())
            {
            bi = new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_3BYTE_BGR
            g = bi.getGraphics();
            paintWave(g, offset, samples_per_pixel,0,w,true);
	}

this is called pretty often (something like every 30 ms to get instant feedback).
Maybe there are much better ways to handle this image?

greetings

basti

They way you have it coded, where it creates a new buffered image every time is not optimal. You should only need to create a new image when the size of the component changes. I don’t think it is changing size every 30ms. Is it?

But even so… I thought the idea was that you were just updating the playhead frequently, and the waveform data much less frequently. is that the case, or is the waveform data that is shown also changing every 30ms?

And, if your are implementing a sideways scrolling waveform display, i.e. that is why it is updating so frequently, then you would want to double buffer the image and only use paintWave() for the new data that scrolls into view, just shift the still visible parts of the old image with a fast blit.

I’m making tons of assumptions because I don’t have a clear idea of what is really going on with this component. Maybe a better description would help.

Scott

You’re right, I should give much more information, sorry. The method redrawAll() checks for changes which make it necessary to redraw the whole image… if redrawAll() return false,
paintWave gets the g from the paintComponent and just draws the area round the marker.
The marker travels from left to right, I scroll the waveform page-wise (or if dragged by the user) so I assume that there’s not advantage of shifting the old image an draw just the playahead. Hope I described it better this time… even though it’s rather late here in germany :wink: .
Thanks for your patience!

basti

[quote=“lebasti,post:6,topic:25253”]
If the “pages” have no overlap in the wave data then you are correct. But do you really flip pages every 30ms? I’m confused about why rendering the wave data to a BufferedImage once every “page” and then just doing a couple blits around the play head (with a clip rect), wouldn’t perform reasonably well.

Also, assuming the playhead motion is predicatable, you can compute the next page in a worker thread while the playhead is updated on the current page… so the transition from one page to another is fast.

No, I only flip the pages when the marker reached the right border of the screen, so the do not overlap at all… is there a “cheaper” way to draw a picture (I mean the BufferedImage i created before) every frame (30ms) than this way:


Graphics2D g2 = (Graphics2D) g;
g2.drawImage(bi,null,0,0); 

I don’t think it’s necessary for me to calcualte the next “pages” in advantage cause it’s ok for the app to have some CPU-spikes… what counts is that the steady cpu-consumption is pretty low (mainly because I don’t want to hear my notebooks fan :wink:

PS. I got some really strange behaviour in sense of cpu-usage… when I dispay the first “page” of the waveform it’s rather high (near 100%) - when it comes to the second page it’s only around 40% … it raises again when I zoom in the waveform. strange.

greetings

basti

I’ve attached a sample application (as source, rename the files .java) that draws a waveform with a moving playhead. I can barely get it to register any CPU usage. Maybe it isn’t doing what you want, but check it out and see.

btw… anyone know why I’m not allowed to upload a .zip or .jar or even .java file? Looks like someone’s brain-dead idea of security.

Dunno. Maybe an attempt to control hd usage? (Which woudl be silly since ist ona box in my lab with plenty of hard drive.)

I’ll ask Chris to look into it.

JK

Thank you for the code! It’s in general the same procedure as in mine, except for the creation of the BI … I’ll update my code today and test it.

Greetings

bas

Before I go ahead and implement this - is there really no way to convince Java to just not clear my JPanel? I used to use that strategy to avoid significant amounts of processor use when double-buffering really wasn’t necessary and I wanted to leave trails. Granted, not so many people have processors slow enough for it make an enormous difference these days, but still…

(I thought from what I’d read that simply not calling super.paintComponent() from my paintComponent() method would do it, but no, and neither does turning double-buffering off… :-\ )

There was a good article onthe Swing rendering pipeline that explained what methods did what, e.g. update(), paint(), paintComponent() etc… I’m sure it is on java.sun.com somewhere.

What I would do is set a breakpoint in your paintComponent and then look back on the call stack and check out the source code for the Swing methods you find, like paint() and update(). You will probably see where Swings backbuffer is filled (update, I think)

Also, there is a method to turn off Swing’s double buffering for a component, I think… setDoubleBuffered… read the java docs for that… they hint that the parent components back buffer may be used… perhaps IT is what gets cleared?

Thanks - I think I read the tutorial you’re talking about, which is probably one of the places I got the idea that overriding paintComponent without super.paintComponent in it ought to do the trick - it suggests calling the super version to clear the component, but in practice it still seems to get cleared even when you don’t. I was surprised that setDoubleBuffered(false) doesn’t seem to help either. :-/

I’ll try re-reading the java docs for that, anyway, maybe I missed something… cheers!

Harrum… well, I did some more re-reading, and thought maybe overriding the

update

method so that it does nothing but call

paint

would help… and it kinda does, except that I now get any Swing component I interact with drawn in the top left-hand corner of the drawing area.

What?!

Here’s a link to the weirdly broken version of the applet , so you can see what I’m talking about. The GUI is supposed to be in a separate JPanel from the drawing, in the east border of the main contentPane…

Worked correctly on the Mac. (Though the controls were crammed for space vertically)

I should have specified that I only get this weird behaviour for the settings where super.paintComponent doesn’t get called - that is, it’s fine except for the ‘with trails’ settings (in the ‘history’ menu). Does it still seem okay on the Mac?

I am realising that Swing implementations are not quite as consistent as I had been led to believe, in any case… :confused:

It was workign fine with all of the settings as far as I could tell… but Swing on Mac is significantly different than on Windows and Linux. The Mac UI has it’s own double buffering etc…