开发者

java+Swing: efficient overlay of rectangle or other "sprite"

I'm not quite sure how to phrase this, so bear with me.

I have two JPanels in a container JPanel with an OverlayLayout. Both JPanels in the container override paint(Graphics).

The bottom JPanel is opaque and draws some fairly complicated graphics, so it takes a "long" time (10s or 100s of milliseconds) to render.

The top JPanel is transparent and just dra开发者_如何学Cws a rectangle or line or simple shape based on mouse input, so it's really quick.

Is there a way to set things up so when I change the simple shape in the upper panel, it doesn't redraw the bottom panel? (e.g. it somehow caches the bottom panel)

I'm vaguely familiar w/ concepts like bitblt, double-buffering, and XOR-drawing but not really sure what to apply here.


You'd be best off using a single JComponent and creating a BufferedImage to store the bottom image. When the paintComponent operation happens on the JComponent, you just blit the bottom image and use the Graphics object to do any further drawing on top of that (from a stored state). Should be fairly efficient.

You'll want to do the complex drawing operations for the bottom BufferedImage in another thread, as the other poster mentions (omitted this by accident, sorry :)). However, you don't want to cause contention on this image, so you must store an additional BufferedImage for this, and blit it synchronously to the other image at the very moment the drawing operations are complete on it.


Focusing on the complicated panel, the key is factoring everything out of paintComponent() except drawImage(). Put everything else in another thread that continually updates an offscreen buffer. Periodically update the screen at some rate that keeps the simple panel responsive. The only hard part is synchronizing, but SwingWorker is a good choice. There's more here.


What's sure is that if the upper panel is target for a full repaint(), then the lower one will be also.

Maybe you can try to optimize the region to repaint on the upper panel in order to avoid repainting all the lower one. But if the painted rectangle in the upper panel covers the whole area, then you end up with full repaint() once again.

Normally, Swing tries to optimize the regions that need a repaint, but it also aggregates these regions when several repaint are performed in a short time, and if I remember well, the aggregated region is just a rectangle that is the union of all repaint rectangles, which is not always optimized but allows for fast computation of repaint events creation.

Now, I think you should follow the advices given in previous replies; indeed, you should really avoid having a paint() method that can perform computations that can be that long (a few 10s of ms should be the real maximum). Painting should be as fast as possible if you don't want to have a GUI that looks unresponsive to the end user. Hence, favour performing the computation only once (and outside the EDT if possible) store the result in a BufferedImage that you just simply draw later on in the paint() method.

EDIT: added other sources of reflection

If you want to optimize the update of the list of points but still keep it in the paint() method, then you can use the clipping region of the passed Graphics to limit the calls to drawing methods, something like:

Rectangle clip = g.getClipBounds();
for (Point p: allPoints) {
    if (clip.contains(p)) {
        // draw p with g graphics
    }
}

You can even try to optimize the list of points to draw by using a QuadTree instead of a simple List, but you'll have to code it yourself (or find some free implementations, there are probably a few of them out there). With a quadtree, you can optimize the time to find the list of all points that have to be redrawn (based on the Graphics clipping rectangle) and only redraw those points.


Addenda for answer by trashgod and jfpoilpret

1/ OverlayLayout is strange way how to layout JPanels, are you same output with once JPanel (without OverlayLayout and Translucentcy)

2/ (10s or 100s of milliseconds) is maybe small value because there is Native OS Latency (45-75ms for today OS and PC)

3/ synchronizations would be managed by using SwingWorker on BackGround Task and with order, directions and synchronizations for painting processes to the JPanel, maybe your paints are too fast/quickly

4/ you didn't describe more about how, where and which about paint()/paintComponent()

    if (SwingUtilities.isEventDispatchThread()) {
        paintImmediately(int x, int y, int w, int h) // or Rectangle r
    } else {
        Runnable doRun = new Runnable() {

            @Override
            public void run() {
                repaint(long tm, int x, int y, int width, int height) // or Rectangle r
            }
        };
        SwingUtilities.invokeLater(doRun);
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜