0

I am trying to prevent redrawing an animation by the EDT. The first thing i have done is excluding the actual drawing tasks into a different thread, writing into a VolatileImage, which gets redrawn by EDT within paintComponent method of my corresponding JPanel.

If i exclude the repaint into another thread, this works properly. Nevertheless, i do have positioned a couple of other panels above my animation. In result, having called the repaint method of my painter (animation) panel, has caused the others to also get redrawn without flickering.

Therefore to redraw the other panels, calling repaint on painter, results in flickering. Repaint of a single panel results in an opaque redraw with rarely flickering.

Does somebody know, how to synchronize an own repaint of a jpanel, for instance into my already available bufferimage. Id say the repaint triggered to EDT results in flickering, since its not synchronized.

My repaint call to animation

 @Override
public void KeyframeChanged(Keyframe frame) {

    if (painter.isVisible()) {
        map.getMainMap().doPaintComponent(painter.getBuffer().getGraphics());

        painter.renderAnimation();                        
        painter.updateScreen();                   
    }
}

painter methods:

 public void updateScreen() {

   Graphics g = this.getGraphics();
    if (g != null) // component already visible?
    {
        // is there a backBuffer to draw?
        if (backBuffer != null) {
            g.drawImage(backBuffer, 0, 0, null);
        } else {
            // if not, create one and render on it                
            createBackBuffer();
            renderAnimation();
        }
    }
}

public void renderAnimation() {

            // Do drawing stuff here
    }

  @Override
protected void paintComponent(Graphics g) {
  //  super.paintComponent(g);               
}// end of paint

Thanks

Thanks for answers and links. I still need to read a few of them. Nevertheless in order to illustrate the current behavior, this small SSCCE shall help.

package repaintexample;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.VolatileImage;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

class Painter extends JPanel {

private VolatileImage backBuffer;
private Graphics2D g2d;

public Painter() {

    setDoubleBuffered(false);
    setOpaque(false);

}

@Override
public boolean isOptimizedDrawingEnabled() {
    return false;
}

private void createBackBuffer() {
    backBuffer = this.getGraphicsConfiguration().createCompatibleVolatileImage(1920, 1200);
}

public void adjustBackBufferSize() {
    if (backBuffer != null) {
        if (getWidth() > backBuffer.getWidth() || getHeight() > backBuffer.getHeight()) {
            createBackBuffer();
        }
    }
}

public void updateScreen(Graphics g) {

    if (g != null) // component already visible?
    {
        // is there a backBuffer to draw?
        if (backBuffer != null) {
            g.drawImage(backBuffer, 0, 0, null);
        } else {
            // if not, create one and render on it                
            createBackBuffer();

        }
    }
}

public void renderAnimation(int i, int j) {


    if (backBuffer == null) {
        createBackBuffer();
    }

    do {


        if (backBuffer.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE) {
            createBackBuffer();
        }

        g2d = (Graphics2D) backBuffer.getGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(Color.white);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        g2d.setColor(Color.red);
        g2d.fillOval(i, j, 50, 50);


    } while (backBuffer.contentsLost());

}

@Override
protected void paintComponent(Graphics g) {
    //  super.paintComponent(g);        

    updateScreen(g);
}// end of paint

public VolatileImage getBuffer() {
    return backBuffer;
}
}

class ContainerFrame extends JFrame {

private Painter mapPainter;
private JPanel informationPanel; // covers a lot of metainformation - Actually own JTable instance updating the same objects for repainting in each circle
private JPanel controller; // Maptools
private JPanel tabbedPabe; // Change redraw content

public ContainerFrame() {

    this.setSize(1600, 1024);
    this.setVisible(true);
    initComponents();
    initPositions();

    Thread animation = new Thread(new Runnable() {
        @Override
        public void run() {

            // My application is a mapping application, in which i first draw the tiles, before goin on with the "real" animated stuff
            // clearing backbuffer content with g.fillRect(0, 0, getWidth(), getHeight());
            while (true) {
                for (int i = 0; i < mapPainter.getWidth(); i += 100) {
                    for (int j = 0; j < mapPainter.getHeight(); j += 5) {
                        mapPainter.renderAnimation(i, j);

                        int repaintCase = 2;

                        switch (repaintCase) {
                            case 0:
                                // Default case redrawing via EDT, triggering the others in proper order
                                mapPainter.repaint();
                                break;
                            case 1:
                                // case repainting by current Thread - necessity of repainting above positioned panels 
                                // results in flickering, since not synchronized
                                mapPainter.updateScreen(mapPainter.getGraphics());
                                informationPanel.repaint();
                                controller.repaint();
                                tabbedPabe.repaint();
                                break;
                            case 2:
                                // painting components on buffer
                                // Results in rarely flickering and opague repaint
                                // is there any common way, to manually repaint onto anything - like image

                                informationPanel.paintAll(mapPainter.getBuffer().getGraphics());
                                controller.paintAll(mapPainter.getBuffer().getGraphics());
                                tabbedPabe.paintAll(mapPainter.getBuffer().getGraphics());
                                mapPainter.updateScreen(mapPainter.getGraphics());

                                break;
                        }

                    }
                }

                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    animation.start();
}

private void initComponents() {
    mapPainter = new Painter();
    mapPainter.setSize(this.getSize());

    informationPanel = new JPanel();
    informationPanel.setSize(new Dimension(360, 800));

    controller = new JPanel();
    controller.setSize(new Dimension(500, 250));

    tabbedPabe = new JPanel();
    tabbedPabe.setSize(new Dimension(300, 300));

    this.getLayeredPane().add(mapPainter, JLayeredPane.DEFAULT_LAYER);
    this.getLayeredPane().add(controller, JLayeredPane.MODAL_LAYER);
    this.getLayeredPane().add(tabbedPabe, JLayeredPane.MODAL_LAYER);
    this.getLayeredPane().add(informationPanel, JLayeredPane.MODAL_LAYER);

}

private void initPositions() {

    controller.setLocation(mapPainter.getWidth() - controller.getWidth(), mapPainter.getHeight() - controller.getHeight());
    tabbedPabe.setLocation(this.getWidth() - tabbedPabe.getWidth(), mapPainter.getHeight() - controller.getHeight() - tabbedPabe.getHeight() - 400);
    informationPanel.setLocation(10, mapPainter.getHeight() - informationPanel.getHeight() - 200);
}
}

public class RepaintExample {

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    // TODO code application logic here
    ContainerFrame f = new ContainerFrame();
}
}

I do use case 0 at the moment and do see having either great or pretty bad fps - either 30 or around 6. I am not certain, how that may be possible and i may be able to find sth. in the already posted links. I thought making sure to relieve the EDT at best, could become a proper solution.

Additionally the content of the 3 panels, i illustrated do not need a repaint in the same frequency as the animation does. Unfortunately i haven't found a proper way to prevent the repaint. The only way i have used for quite a while was a paintimmediately in an invokelater call for those areas, which are known as "animated". A common repaint(Rectangle rec) has not been working, since single calls have been summarized to a big one, covering more pixels, than i have passed in.

public void drawCachedSprite(Graphics2D g, CachedSprites sprites, int zoom, double cog, double x, double y, double w, double h) {

    try{
    pos_x = x;
    pos_y = y;

    RenderingUtil.getRenderQuality();

    transform.setToIdentity();
    // Compute the corner, the drawing needs to start with           
    transform.translate(x - (w / 2.0), y - (h / 2.0));
    g.drawImage(sprites.getSprite(DefaultResources.getType(), spriteColor, zoom, cog), transform, null);
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
        System.out.println("width or height not set properly");
    }
}
user2148322
  • 65
  • 2
  • 9
  • 2
    Swing components are double buffered by default. You never be calling `getGraphics`, apart from the fact that this can return `null`, anything painted to will be discard on the next repaint. You should be painting you buffer in the `paintComponent` method – MadProgrammer May 23 '13 at 08:05
  • I am trying to prevent redrawing an animation by the EDT this could be possible only by override RepaintManager, but I'm not going this way, please whats goal, use 1) only one Swing Timer for all Objects 2) use BufferedImage 3) put all Object(s) to the array, loop inside array in paintComponent 4) point from comment by MadProgrammer – mKorbel May 23 '13 at 08:09
  • 1
    for better help sooner post an [SSCCE](http://sscce.org/), short, runnable, compilable – mKorbel May 23 '13 at 08:11
  • Well that seems to slow down my application. Thatswhy i want to prevent that. In addition i would like to make sure repainting only as soon as the content of my buffer has been changed. Having the repaint handled by EDT would open up possibility repainting not as needed. In addition sometimes the whole browser freezes (its an applet), which i guess happens if sth is goin wrong with EDT. Having the repaint in my own threads, results in way more control and debugging options. – user2148322 May 23 '13 at 08:13
  • Well i dont mind repainting in paintComponent method. I am just wondering, why my application sometimes seems way slower and sometimes totally fluently. – user2148322 May 23 '13 at 08:39
  • 1
    nobody know, there isn't place for speculations, everything here could be based on your SSCCE, short, runnable, compilable, especially @MadProgrammer is top answerer here in this area, most of his answers contains great code, to try to search here – mKorbel May 23 '13 at 09:01
  • 1
    With out a demonstrable code we won't know. Take a look at [this](http://stackoverflow.com/questions/14886232/swing-animation-running-extremely-slow/14902184#14902184), it animates up to 4500 images on the screen and demonstrates some ideas for performance improvements – MadProgrammer May 23 '13 at 09:46
  • 1
    You could also check out [this](http://stackoverflow.com/questions/13022754/java-bouncing-ball/13022788#13022788), [this](http://stackoverflow.com/questions/14593678/multiple-bouncing-balls-thread-issue/14593761#14593761), [this](http://stackoverflow.com/questions/14432816/how-to-move-an-image-animation/14436331#14436331), [this](http://stackoverflow.com/questions/13540534/how-to-make-line-animation-smoother/13547895#13547895) which deal with flickering and performance issues... – MadProgrammer May 23 '13 at 09:51
  • Hm seems like the EDT is not the reason, why my application sometimes gets slowed down. For some reason the redraw of my sprite image is sometimes pretty expensive -around 1ms for a single repaint of a sprite, while sometimes its around 0.03ms. Any idea, how that can become caused? See edits. – user2148322 May 24 '13 at 12:25
  • Why not go for the full Graphics2D package, and throw away the Swing-stuff? Maybe not for the menus, but for things that need to be updated fast/constantly. If you make your own gameloop to govern every update, all in a single thread, you can have full control over the animations, and everything else, on any number of canvases simultaneously. I made prototype-project, with a simple gameloop, that draws a single image (map) on a Canvas, and has Swing GUI's around it. The canvas and Swing-components can talk to eachother, and share information easily. I can send if you want. – Ultroman the Tacoman Dec 07 '13 at 00:12

0 Answers0