Swing双缓冲和动画

时间:2013-05-23 07:57:42

标签: java swing animation repaint

我试图阻止EDT重绘动画。我做的第一件事是将实际的绘图任务排除在另一个线程中,写入一个VolatileImage,它在我对应的JPanel的paintComponent方法中由EDT重绘。

如果我将重绘排除到另一个线程,这可以正常工作。不过,我确实在我的动画上方放置了几个其他面板。 结果,调用了我的画家(动画)面板的重绘方法,导致其他人也重新绘制而没有闪烁。

因此,重绘其他面板,在画家上调用重绘会导致闪烁。重新绘制单个面板会导致不透明的重绘,很少闪烁。

有人知道,如何将自己的jpanel重绘同步到我已经可用的缓冲图像中。我说重新启动触发EDT导致闪烁,因为它没有同步。

我重新调用动画

 @Override
public void KeyframeChanged(Keyframe frame) {

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

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

画家方法:

 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

由于

感谢您的回答和链接。我仍然需要阅读其中的一些内容。然而,为了说明当前的行为,这个小型SSCCE应该有所帮助。

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();
}
}

我现在使用案例0确实看到有大或非常差的fps - 30或大约6.我不确定,这怎么可能,我可以找到某事。在已发布的链接中。我认为确保最多可以减轻EDT,可以成为一个合适的解决方案。

此外,我所示的3个面板的内容不需要以与动画相同的频率重新绘制。不幸的是,我还没有找到一种防止重绘的正确方法。我已经使用了很长一段时间的唯一方法是在一个叫做“动画”的那些区域的invokelater调用中的痛苦。常见的重绘(Rectangle rec)一直没有用,因为单个调用已经汇总为一个大的,覆盖的像素比我传入的更多。

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");
    }
}

0 个答案:

没有答案