Skip to main content

Painting optimizations for your LayerUI

No replies
alexfromsun
Offline
Joined: 2005-09-05
Points: 0

Hello Everybody

Here I'd like to shed light on one of the JXLayer's feature which I didn't mention in my blogs before,
when I wrote this component I kept in mind that it should have all kind of optimizations
to make it possible to write fast animation effects, here I'd like to explain how to repaint JXLayer in optimized manner.

As you know to repaint JXLayer you should call setDirty(true) from your LayerUI,
however it will repaint *the whole layer* which may be expensive.
What if you need to repaint only a part of it?

To answer this question let me explain how setDirty(true) works:

<br />
    // code from AbstractLayerUI<br />
    protected void setDirty(boolean isDirty) {<br />
        boolean oldDirty = isDirty();<br />
        this.isDirty = isDirty;<br />
        if (isDirty && !oldDirty) {<br />
            fireLayerItemChanged();<br />
        }<br />
    }<br />

As you can see it sets the "dirty" flag and calls the fireLayerItemChanged(); method
that notifies all JXLayers of this UI to repaint themselves

Actually if your LayerUI doesn't extend AbstractBufferedLayerUI,
there is no difference between setDirty(true) and fireLayerItemChanged()
both of them do the same - repaint all JXLayers of this LayerUI

The only case when you can see the difference is descendants of AbstractBufferedLayerUI
when they are not is incremental update mode
(boolean isIncrementalUpdate(JXLayer l) returns false)

In this case fireLayerItemChanged() will call jxlayer.repaint() but the UI's back buffer will not be updated and setDirty(true) will update the buffer and repaint the layer

You can use the "dirty" flag in a situation when your painting code needs to know
is it you initiated the repaint with setDirty(true) method
or it was triggered by another action like repainting of layer's child component

And now the main trick: there is the fireLayerItemChanged which takes LayerItemChangeEvent as the parameter,
to update a part of any layer's for this UI you should customize this event:

<br />
                   // use it instead of setDirty(true)<br />
                   fireLayerItemChanged(new LayerItemChangeEvent(this) {</p>
<p>                    @Override<br />
                    public Shape getClip(int width, int height) {<br />
                        // This will repaint only quorter of a JXLayer<br />
                        return new Rectangle(width/2, height/2);<br />
                    }<br />
                });<br />

This trick will help you to optimize the painting of JXLayer and make it as fast as possible

Thanks
alexp