active rendering and swing

hi,

what is the correct way of active rendering in swing ? such an animation loop doesnt cause the component (a custom JPanel) to be repainted immediately ?


while(running) {
    // game and render stuff here
    //..
    component.repaint(); // this wont make the component to be painted immediately, but later
}

repaint(0) wont help either since loop will continue running and repaint(0) requests will coalesce

if component.repaint() is replaced with


component.paintImmediately(component.getBounds());

this also fails since animation loop doesnt run in AWT-Thread’s context. If we run these in AWT-Thread then we block the AWT-Thread and cause AWT and Swing events not to be dispatched. i looked for a method to dispatch all waiting AWT events and return but couldnt find.

well, infact running this loop in another thread will succesfully and actively draw the component but that way we lack swing layout features. to be precise, i use the component at bottom of a JLayeredPane and there are other components on top of that. since we call component.paintImmediately() in another thread swing gets out of sync and those other components disappear or flicker

the only solution i can think of is to use:


SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        component.paintImmediately(component.getBounds());
    }
});

instead of component.repaint(). but this is error-prone and one can easily fall into deadlock trap (as i did ::)), and worse enough it is too expensive because of context switching

so what is the solution ?

thx
r a f t

In order to do active rendering u must get Graphics of desired component with getGraphics() method. I used to do this only with frames, but panels can do fine i think.
Then u use normal drawing procs and it will apear on the screan instantly(actively rendered).
Note that u should do double buffering.

If you’re using 1.4 or later, the BufferStratergy class lets you have manual control over a Canvas or Window’s display surface, including when it gets swapped to screen. Easiest way to do active rendering.

sorry for late answer. some how forum engine didnt notify me ???

if you do rendering over what getGraphics() return, then you loose swing layout features. compile and run the following sample. the JLabel over the custom graphics component always remains there, without flicker. then run it the other way with getGraphics, JLabel will dissapper or flicker

note this is a basic sample so calling paintImmediately either in AWT-Thread or not does not matter that much. in a more complicated gui, swing simply gets out of sync. that part shows up here etc


import java.awt.*;
import javax.swing.*;

public class Active extends JPanel {
    
    public void run() {
        while (true) {
            //render(getGraphics()); // --- > uncomment this 
            paintImmediately(getBounds()); // --> comment this
            try {
                Thread.sleep(10);
            } catch (Exception e) {}
        }
    }
    protected void paintComponent(Graphics g) {
        render(g);
    }
    
    int textPosition = 0;
    void render(Graphics g) {
        Dimension size = getSize();
        
        g.setColor(Color.ORANGE);
        g.fillRect(0, 0, size.width, size.height);
        
        textPosition += 3;
        textPosition = (textPosition > size.width) ? 0 : textPosition;
        
        g.setColor(Color.RED);
        g.drawString("moving string", textPosition, textPosition);
    }
    
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame("active rendering test");
        JLayeredPane layeredPanel = new JLayeredPane();

        Active active = new Active();
        layeredPanel.add(active, JLayeredPane.DEFAULT_LAYER);
        active.setBounds(0, 0, 400, 400);
        
        JLabel label = new JLabel("a long long label");
        layeredPanel.add(label, JLayeredPane.DRAG_LAYER); // above active
        label.setBounds(0, 0, 200, 40);
        
        layeredPanel.setPreferredSize(new Dimension(400, 400));
        frame.add(layeredPanel);
        frame.pack();
        frame.setVisible(true);
        
        active.run();
    }
}

r a f t
have a look at my application at www.aptalkarga.com you will see a deep hierarchy of semi-transparent components (even a JLayeredPane in a JLayeredPane) where all depend on swing layout managers

edit: updated the link

i use java5. i cant use Canvas since i use other swing components over my graphics component. if you mix awt and swing components in a JLayeredPane, awt components simply hide swing ones independent of their layers (heavy vs light-weigt)

similarly i cant use BufferStrategy since there seems no way (or i couldnt find yet) to get a BufferStrategy for a swing component, except top level containers. i dont want to draw to JFrame’s surface but to a JPanel’s as i need layering described above

r a f t