Win a copy of Murach's Python Programming this week in the Jython/Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Responsibility for buffering?  RSS feed

 
Patrick Wright
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi

I've started to read Filthy Rich Clients and I have (at least) one question related to buffering in Swing. The Swing library will normally buffer individual components into a back-buffer without any extra coding on the programmer's part. But what if you're taking control of an entire panel and rendering directly using Graphics2D? I'm thinking about this in terms of Flying Saucer, wondering if we are doing the right thing. What we don't do is create 1 Swing component for every renderable, positionable item that appears on a page, except for form elements and images--there is an in-memory rendering and layout model, but it's used to direct the direct rendering operations themselves. When Swing (and not, say, PDF) is the output target, we're working with a single JPanel subclass embedded in a JScrollPane.

It seems to me that the RepaintManager won't be able to track what changes--but I assume the entire panel is still double-buffered automatically?

The rendering engine we have is not my domain but the question still interests me...

A separate (but for me, related) question is how ScrollPanes are optimized--how much of the scrollable content is in memory, when it gets there, if it's buffered, etc.


TIA
Patrick
 
Chet Haase
author
Ranch Hand
Posts: 32
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Patrick,

I'm not following what's different about your situation from any other Swing component - you have contents in a JPanel and the contents are rendered with Graphics2D. How is this different? I'm sure I'm just missing some details here, maybe you could fill them in for me.

As for JScrollPane optimization, it is not separately buffered (the only
buffer Swing uses is one the size of the window that maps exactly
onto the pixels on the screen). But any scroll operation consists of a simple copy of the area that has not changed plus an update of the new area. See the explanation of copyArea in chapter 3 (I think) for more
information about this.

Chet.
 
Patrick Wright
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Chet

Thanks for the reply. I asked the question in part because I was having trouble understanding when the buffering takes place. If I follow you correctly, graphics operations on the Graphics instance passed to any JComponent are written directly to the back buffer, and there is only 1, large, back buffer. Is that more or less it? I was under the impression that there was 1 buffer per component, and that we might be disabling it by taking control of the full panel rendering space.

What was confusing to me is that while on one level, you could say we have lightweight components (as in really lightweight, since we need so many of them to satisfy the layout model), Swing just treats it as a JPanel which is handling its own rendering, and buffers it just like any other component.

As a follow-up to that, this also means that as far as the standard RepaintManager is concerned, if a change takes place within our rendering model (that's the model that corresponds to items on-screen, like lines of text and such) the RM won't be able to track what exactly changed, since it only has access to the single "dirty" state of the whole panel. Right now the only "dynamic" changes we support are related to mouse hover over elements, and only support model changes that don't affect the layout--things like changing the background color or border color. Still, we have the issue that as the mouse moves over the panel, it may trigger a change in just one element of our model and in principle we would want only that region to be updated.

However, as a plugin library, e.g. as a utility people use within a larger application, it seems wrong for us to install a custom RepaintManager which could track smaller dirty regions within our panel. Our model elements can track that, though. What is the correct way to approach this? I'm still confused about the relationship between repaint (when a component is dirty) and the back buffer. Ideally we'd like everything in the buffer to remain the same, and only repaint the area in our panel affected by the hover. The performance of this in fact is very good, but I'd like to understand the responsibilities, strategies and optimizations people are recommended to use.

Here's an example. Suppose I'm writing a game of life program where I want to draw the entire grid (10 * 10) myself. I have a model that can track each cell in the grid and paint each cell individually. In the first generation I start out with three living cells at 0,0 to 0,2. In generation 2, the model is updated so that cell 1,1 is alive. The panel is now dirty (from Swing's perspective). What is my responsibility, as component owner, to redraw? Do I have to keep my own back buffer for the panel and update the just clip for just the affected cell on my buffer, then copy the buffer back on to the Graphics instance?

TIA
Patrick
 
Chet Haase
author
Ranch Hand
Posts: 32
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Patrick,

I think I'm following this more now. Here's my take on it:

- As you said, you probably don't want to install a RepaintManager as a plugin provider. I think there's one per top-level window, so it'd be a bit rude to install yours when the rest of the app might want something different. It would be like staying at someone's house for a weekend and painting their house purple because you happened to like it.

- You could buffer your own component (thus getting buffering at the component level, instead of at Swing's window level), but you're starting to blow memory and buffer management headache that you really don't need to.

- You should (I think) use the clip. Whenever something changes in your component that requires you to tell Swing to repaint, the easiest way to cause the repaint is to call repaint() on the component. That will cause the entire component to redisplay itself. But if you only need to repaint a small portion of that component, then call repaint(x, y, w, h) instead. This will cause Swing to set the Clip on the Graphics object prior to calling the paintComponent() method. And if you're clever about checking the clip, you can avoid repainting items that fall outside of the clip. (Note that this optimization makes the most sense if the items not rendered are actually expensive to render, otherwise it may not buy you much since Java 2D can draw stuff pretty fast). There's more about this in the "Use the Clip" section of Chapter 5, for those that have the book. But that's the basic approach: only schedule repaints for the areas that need it, and pay attention to the Clip that Swing hands you.

Chet.
 
Romain Guy
author
Ranch Hand
Posts: 47
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Patrick,

Dirty regions are marked dirty when you call repaint(x, y, w, h). The RepaintManager is not scrutinizing your components to find out what changed. The components/the application tell the RepaintManager what is dirty. Simply put: forget about the RepaintManager.

. If I follow you correctly, graphics operations on the Graphics instance passed to any JComponent are written directly to the back buffer, and there is only 1, large, back buffer. Is that more or less it?


That's it.

I was under the impression that there was 1 buffer per component, and that we might be disabling it by taking control of the full panel rendering space.


Nope.

Now, in some cases you might want to create your own buffer for a specific component. Even though there's a global buffer in Swing, there might be cases in which Swing will ask your component to repaint itself into the buffer. At that time, your component's content might not have changed, but Swing (ie. the RepaintManager) does not know that. In most cases, you simply repaint the component with Java 2D commands (drawLine(), etc.) It is fast enough. In some cases, when the rendering code is very heavy, you instead use a local buffer.

A local buffer also allows you to do background rendering. In the case of an HTML renderer, painting can be very complex. So what you would do is this:



This technique is exposed in the book under the name "intermediate image."

In the first generation I start out with three living cells at 0,0 to 0,2. In generation 2, the model is updated so that cell 1,1 is alive. The panel is now dirty (from Swing's perspective). What is my responsibility, as component owner, to redraw? Do I have to keep my own back buffer for the panel and update the just clip for just the affected cell on my buffer, then copy the buffer back on to the Graphics instance?


The panel is not dirty until you tell Swing so with a call to repaint(). When your model changes, you need to call repaint() or better yet, repaint(x, y, w, h). You can issue several calls to repaint(x, y, w, h) and the RepaintManager will try to coalesce them by merging the dirty regions.

Then in the paintComponent method take into account the clip. This clip basically comes from the arguments you passed to repaint(). Just redraw the cells that intersect with the clip.

This is important because in some cases Swing will repaint the whole frame or the whole component, even though there's a back buffer. This happens for instance when you have a non-opaque component on top of your component (for instance, a glass pane).
 
Patrick Wright
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks to both of you for your replies. I now have a better idea of what's involved. I've started to read the sections in the book that you referred to, also very helpful.

Cheers!
Patrick
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!